From 6065a59a6f135ee96429132c9fce9a0fbb22e8ba Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 13 May 2024 16:29:39 +0200 Subject: [PATCH 001/126] CSDK-125 new PurseIdentifier type Signed-off-by: David Hernando --- Casper.Network.SDK/Types/GlobalStateKey.cs | 14 +++++++++++++- Casper.Network.SDK/Types/IPurseIdentifier.cs | 13 +++++++++++++ Casper.Network.SDK/Types/PublicKey.cs | 14 +++++++++++++- Casper.Network.SDK/Types/URef.cs | 14 +++++++++++++- 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 Casper.Network.SDK/Types/IPurseIdentifier.cs diff --git a/Casper.Network.SDK/Types/GlobalStateKey.cs b/Casper.Network.SDK/Types/GlobalStateKey.cs index aef55ce..727d079 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; @@ -281,7 +282,7 @@ public override void Write( /// Stores an account in the global state. /// Format: 32-byte length with prefix 'account-hash-'. /// - public class AccountHashKey : GlobalStateKey + public class AccountHashKey : GlobalStateKey, IPurseIdentifier { public static string KEYPREFIX = "account-hash-"; @@ -294,6 +295,17 @@ public AccountHashKey(PublicKey publicKey) : base(publicKey.GetAccountHash(), KEYPREFIX) { } + + /// + /// Returns a PurseIdentifier object as defined in the RPC schema for an account hash key. + /// + public Dictionary GetPurseIdentifier() + { + return new Dictionary + { + {"main_purse_under_account_hash", this.ToString()} + }; + } } /// diff --git a/Casper.Network.SDK/Types/IPurseIdentifier.cs b/Casper.Network.SDK/Types/IPurseIdentifier.cs new file mode 100644 index 0000000..1849501 --- /dev/null +++ b/Casper.Network.SDK/Types/IPurseIdentifier.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace Casper.Network.SDK.Types +{ + /// + /// A PublicKey, AccountHashKey, AddressableEntityKey or URef object that identifies a purse. + /// For keys, the main purse under the key is identified automatically. + /// + public interface IPurseIdentifier + { + public Dictionary GetPurseIdentifier(); + } +} diff --git a/Casper.Network.SDK/Types/PublicKey.cs b/Casper.Network.SDK/Types/PublicKey.cs index 7228b40..5d883b5 100644 --- a/Casper.Network.SDK/Types/PublicKey.cs +++ b/Casper.Network.SDK/Types/PublicKey.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; @@ -17,7 +18,7 @@ namespace Casper.Network.SDK.Types /// /// A wrapper for a Public Key. Provides signature verification functionality. /// - public class PublicKey + public class PublicKey: IPurseIdentifier { /// /// Byte array without the Key algorithm identifier. @@ -264,6 +265,17 @@ public bool VerifySignature(string message, string signature) { return VerifySignature(Hex.Decode(message), Hex.Decode(signature)); } + + /// + /// Returns a PurseIdentifier object as defined in the RPC schema for a public key. + /// + public Dictionary GetPurseIdentifier() + { + return new Dictionary + { + {"main_purse_under_public_key", this.ToString()} + }; + } #region Cast operators diff --git a/Casper.Network.SDK/Types/URef.cs b/Casper.Network.SDK/Types/URef.cs index e69ddc6..bc3afc8 100644 --- a/Casper.Network.SDK/Types/URef.cs +++ b/Casper.Network.SDK/Types/URef.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using Casper.Network.SDK.Utils; using Org.BouncyCastle.Utilities.Encoders; @@ -10,7 +11,7 @@ namespace Casper.Network.SDK.Types /// except Account. Additionally, URefs used in contracts carry permission information /// to prevent unauthorized usage of the value stored under the key. /// - public class URef : GlobalStateKey + public class URef : GlobalStateKey, IPurseIdentifier { public static string KEYPREFIX = "uref-"; @@ -77,5 +78,16 @@ public override string ToString() { return KEYPREFIX + CEP57Checksum.Encode(RawBytes) + $"-{(byte) AccessRights:000}"; } + + /// + /// Returns a PurseIdentifier object as defined in the RPC schema for an URef key. + /// + public Dictionary GetPurseIdentifier() + { + return new Dictionary + { + {"purse_uref", this.ToString()} + }; + } } } From 904aa294af73e0941007f46a5fd8d647770fd89a Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 13 May 2024 16:31:58 +0200 Subject: [PATCH 002/126] CSDK-126 BlockIdentifier type Signed-off-by: David Hernando --- Casper.Network.SDK/Types/BlockIdentifier.cs | 49 +++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Casper.Network.SDK/Types/BlockIdentifier.cs diff --git a/Casper.Network.SDK/Types/BlockIdentifier.cs b/Casper.Network.SDK/Types/BlockIdentifier.cs new file mode 100644 index 0000000..19412d9 --- /dev/null +++ b/Casper.Network.SDK/Types/BlockIdentifier.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; + +namespace Casper.Network.SDK.Types +{ + /// + /// A block identified by its hash or its height. + /// + public interface IBlockIdentifier + { + public Dictionary GetBlockIdentifier(); + } + + /// + /// A block identified by its hash or its height. + /// + public class BlockIdentifier : IBlockIdentifier + { + private string _hash; + private UInt64? _height; + + public BlockIdentifier(string hash) + { + _hash = hash; + } + + public BlockIdentifier(UInt64 height) + { + _height = height; + } + + /// + /// Returns a BlockIdentifier object as defined in the RPC schema for a block. + /// + public Dictionary GetBlockIdentifier() + { + if(_height.HasValue) + return new Dictionary + { + { "Height", _height.Value}, + }; + + return new Dictionary + { + { "Hash", _hash}, + }; + } + } +} \ No newline at end of file From 52eb681e9b3e9357fdb64dfa4d5f250587a7f808 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 13 May 2024 16:33:04 +0200 Subject: [PATCH 003/126] CSDK-127 new QueryBalanceDetails class in CasperMethods Signed-off-by: David Hernando --- Casper.Network.SDK/JsonRpc/CasperMethods.cs | 47 +++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/Casper.Network.SDK/JsonRpc/CasperMethods.cs b/Casper.Network.SDK/JsonRpc/CasperMethods.cs index 7dfe949..0c31f38 100644 --- a/Casper.Network.SDK/JsonRpc/CasperMethods.cs +++ b/Casper.Network.SDK/JsonRpc/CasperMethods.cs @@ -226,6 +226,53 @@ public GetBalance(PublicKey key, StateIdentifier stateIdentifier) : base("query_ } } + public class QueryBalanceDetails : RpcMethod + { + /// + /// Query for full balance information using a purse identifier and a state identifier + /// + /// The identifier to obtain the purse corresponding to balance query. + /// The identifier for the state used for the query, if none is passed, the latest block will be used. + public QueryBalanceDetails(IPurseIdentifier purseIdentifier, IBlockIdentifier blockIdentifier) : base("query_balance_details") + { + this.Parameters = new Dictionary + { + {"purse_identifier", purseIdentifier.GetPurseIdentifier()}, + }; + if(blockIdentifier != null) + this.Parameters.Add("state_identifier", new Dictionary + { + { "block", blockIdentifier.GetBlockIdentifier()} + }); + } + + /// + /// Query for full balance information using a purse identifier and a state identifier + /// + /// The identifier to obtain the purse corresponding to balance query. + /// The state root hash used for the query. + /// Timestamp for holds lookup. + public QueryBalanceDetails(IPurseIdentifier purseIdentifier, string stateRootHash, string timestamp) : base("query_balance_details") + { + this.Parameters = new Dictionary + { + {"purse_identifier", purseIdentifier.GetPurseIdentifier()}, + {"state_identifier", + new Dictionary + { + { + "state_root", new Dictionary + { + {"state_root_hash", stateRootHash}, + {"timestamp", timestamp}, + } + }, + } + }, + }; + } + } + public class PutDeploy : RpcMethod { /// From 232cbd0cec87b9008c31341e558fa1a83b5ec2a1 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 13 May 2024 16:33:34 +0200 Subject: [PATCH 004/126] CSDK-128 new QueryBalanceDetailsResult type Signed-off-by: David Hernando --- .../ResultTypes/QueryBalanceDetailsResult.cs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 Casper.Network.SDK/JsonRpc/ResultTypes/QueryBalanceDetailsResult.cs diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/QueryBalanceDetailsResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/QueryBalanceDetailsResult.cs new file mode 100644 index 0000000..e20c0d0 --- /dev/null +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/QueryBalanceDetailsResult.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; + +namespace Casper.Network.SDK.JsonRpc.ResultTypes +{ + /// + /// A hold. + /// + public class Hold + { + /// + /// The amount in the hold. + /// + [JsonPropertyName("amount")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Amount { get; init; } + + /// + /// The block time at which the hold was created. + /// + [JsonPropertyName("time")] + public UInt64 Time { get; init; } + /// + /// A proof that the given value is present in the Merkle trie. + /// + [JsonPropertyName("proof")] + public string Proof { get; init; } + } + + /// + /// Result for "query_balance_details" RPC response. + /// + public class QueryBalanceDetailsResult : RpcResult + { + /// + /// The purses total balance, not considering holds. + /// + [JsonPropertyName("total_balance")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger TotalBalance { get; init; } + + /// + /// The available balance in motes (total balance - sum of all active holds). + /// + [JsonPropertyName("available_balance")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger AvailableBalance { get; init; } + + /// + /// A proof that the given value is present in the Merkle trie. + /// + [JsonPropertyName("total_balance_proof")] + public string TotalBalanceProof { get; init; } + + /// + /// Holds active at the requested point in time. + /// + [JsonPropertyName("holds")] + public List Holds { get; init; } + } +} From f882c13a8e9c813621dd5e4a307fe48820ba4d37 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 13 May 2024 16:34:33 +0200 Subject: [PATCH 005/126] CSDK-129 New methods in CasperClient class. Signed-off-by: David Hernando --- Casper.Network.SDK/NetCasperClient.cs | 38 ++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index d006453..5a66265 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -388,6 +388,43 @@ public async Task> GetAccountBalance(PublicKey pub var method = new GetBalance(publicKey, StateIdentifier.WithBlockHeight(blockHeight)); return await SendRpcRequestAsync(method); } + + /// + /// Queries the balance information including total, available, and holds. + /// + /// A PublicKey, AccountHashKey, URef or EntityAddr to identify a purse. + /// Hash of the block. Null to get latest available. + public async Task> QueryBalanceDetails(IPurseIdentifier purseIdentifier, + string blockHash = null) + { + var method = new QueryBalanceDetails(purseIdentifier, blockHash != null ? new BlockIdentifier(blockHash) : null); + return await SendRpcRequestAsync(method); + } + + /// + /// Queries the balance information including total, available, and holds. + /// + /// A PublicKey, AccountHashKey, URef or EntityAddr to identify a purse. + /// Height of the block. + public async Task> QueryBalanceDetails(IPurseIdentifier purseIdentifier, + UInt64 blockHeight) + { + var method = new QueryBalanceDetails(purseIdentifier, new BlockIdentifier(blockHeight)); + return await SendRpcRequestAsync(method); + } + + /// + /// Queries the balance information including total, available, and holds. + /// + /// A PublicKey, AccountHashKey, URef or EntityAddr to identify a purse. + /// The state root hash used for the query. + /// Timestamp for holds lookup. + public async Task> QueryBalanceDetails(IPurseIdentifier purseIdentifier, + string stateRootHash, string timestamp) + { + var method = new QueryBalanceDetails(purseIdentifier, stateRootHash, timestamp); + return await SendRpcRequestAsync(method); + } /// /// Send a Deploy to the network for its execution. @@ -402,7 +439,6 @@ public async Task> PutDeploy(Deploy deploy) var method = new PutDeploy(deploy); return await SendRpcRequestAsync(method); } - /// /// Request a Deploy object from the network by the deploy hash. From 074ff2f56f4004f22aa1d80825029e3b3769692b Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 13 May 2024 16:51:24 +0200 Subject: [PATCH 006/126] CSDK-115 added latest_switch_block_hash property Signed-off-by: David Hernando --- .../JsonRpc/ResultTypes/GetNodeStatusResult.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetNodeStatusResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetNodeStatusResult.cs index 528b70f..1213eab 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetNodeStatusResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetNodeStatusResult.cs @@ -80,5 +80,10 @@ public class GetNodeStatusResult : RpcResult /// The status of the block synchronizer builders. /// [JsonPropertyName("block_sync")] public BlockSynchronizerStatus BlockSync { get; init; } + + /// + /// The hash of the latest switch block. + /// + [JsonPropertyName("latest_switch_block_hash")] public string LatestSwitchBlockHash { get; init; } } } From 39fd53bff99a7f4ac11819869a520d18b8500502 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 14 May 2024 17:37:24 +0200 Subject: [PATCH 007/126] CSDK-114 chain_get_block CSDK-155 chain_get_block_transfers Signed-off-by: David Hernando --- .../JsonRpc/ResultTypes/GetBlockResult.cs | 4 +- .../ResultTypes/GetBlockTransfersResult.cs | 4 +- Casper.Network.SDK/SSE/BlockAdded.cs | 7 +- Casper.Network.SDK/Types/Block.cs | 360 +++++++++++++++--- Casper.Network.SDK/Types/EraEnd.cs | 61 ++- Casper.Network.SDK/Types/InitiatorAddr.cs | 56 +++ Casper.Network.SDK/Types/TransactionHash.cs | 14 + Casper.Network.SDK/Types/Transfer.cs | 165 +++++++- 8 files changed, 607 insertions(+), 64 deletions(-) create mode 100644 Casper.Network.SDK/Types/InitiatorAddr.cs create mode 100644 Casper.Network.SDK/Types/TransactionHash.cs diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs index 1547c4f..2874985 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs @@ -11,7 +11,7 @@ public class GetBlockResult : RpcResult /// /// The block, if found. /// - [JsonPropertyName("block")] - public Block Block { get; init; } + [JsonPropertyName("block_with_signatures")] + public BlockWithSignatures BlockWithSignatures { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockTransfersResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockTransfersResult.cs index b3e6399..05231b3 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockTransfersResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockTransfersResult.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; using Casper.Network.SDK.Types; namespace Casper.Network.SDK.JsonRpc.ResultTypes @@ -19,6 +20,7 @@ public class GetBlockTransfersResult : RpcResult /// The block's transfers /// [JsonPropertyName("transfers")] - public List Transfers { get; init; } + [JsonConverter(typeof(GenericListConverter))] + public List Transfers { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/SSE/BlockAdded.cs b/Casper.Network.SDK/SSE/BlockAdded.cs index ae96e0e..a0df706 100644 --- a/Casper.Network.SDK/SSE/BlockAdded.cs +++ b/Casper.Network.SDK/SSE/BlockAdded.cs @@ -11,11 +11,14 @@ public class BlockAdded /// /// The Block hash. /// - [JsonPropertyName("block_hash")] public string BlockHash { get; init; } + [JsonPropertyName("block_hash")] + public string BlockHash { get; init; } /// /// The Block data. /// - [JsonPropertyName("block")] public Block Block { get; init; } + [JsonPropertyName("block")] + [JsonConverter(typeof(Block.BlockConverter))] + public IBlock Block { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Block.cs b/Casper.Network.SDK/Types/Block.cs index 0233bfe..ba53a79 100644 --- a/Casper.Network.SDK/Types/Block.cs +++ b/Casper.Network.SDK/Types/Block.cs @@ -3,75 +3,153 @@ using System.ComponentModel; using System.Text.Json; using System.Text.Json.Serialization; +using Org.BouncyCastle.Asn1.X509.Qualified; namespace Casper.Network.SDK.Types { + public abstract class BlockHeader + { + /// + /// Json converter class to serialize/deserialize a Block to/from Json + /// + public class BlockHeaderConverter : JsonConverter + { + public override BlockHeader Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + reader.Read(); + var version = reader.GetString(); + reader.Read(); + switch (version.ToLowerInvariant()) + { + case "version1": + { + var block1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return block1; + } + case "version2": + var block2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return block2; + default: + throw new JsonException("Expected Version1 or Version2"); + } + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + BlockHeader blockHeader, + JsonSerializerOptions options) + { + if (blockHeader is BlockHeaderV2) + { + writer.WritePropertyName("Version2"); + writer.WriteStartObject(); + JsonSerializer.Serialize(blockHeader as BlockHeaderV2, options); + writer.WriteEndObject(); + } + else + { + writer.WritePropertyName("Version1"); + writer.WriteStartObject(); + JsonSerializer.Serialize(blockHeader as BlockHeaderV1, options); + writer.WriteEndObject(); + } + } + } + } + /// /// A block header /// - public class BlockHeader + public class BlockHeaderV1 : BlockHeader { /// - /// Accumulated seed. + /// A seed needed for initializing a future era. /// [JsonPropertyName("accumulated_seed")] public string AccumulatedSeed { get; init; } /// - /// The body hash. + /// The hash of the block's body. /// [JsonPropertyName("body_hash")] public string BodyHash { get; init; } /// - /// The era end. + /// The `EraEnd` of a block if it is a switch block. /// [JsonPropertyName("era_end")] - public EraEnd EraEnd { get; init; } + public EraEndV1 EraEnd { get; init; } /// - /// The block era id. + /// The era ID in which this block was created. /// [JsonPropertyName("era_id")] public ulong EraId { get; init; } /// - /// The block height. + /// The height of this block, i.e. the number of ancestors. /// [JsonPropertyName("height")] public ulong Height { get; init; } - + /// - /// The parent hash. + /// The parent block's hash. /// [JsonPropertyName("parent_hash")] public string ParentHash { get; init; } - + /// - /// The protocol version. + /// The protocol version of the network from when this block was created. /// - [JsonPropertyName("protocol_version")] + [JsonPropertyName("protocol_version")] public string ProtocolVersion { get; init; } - + /// - /// Randomness bit. + /// A random bit needed for initializing a future era. /// [JsonPropertyName("random_bit")] public bool RandomBit { get; init; } - + /// - /// The state root hash. + /// The root hash of global state after the deploys in this block have been executed. /// [JsonPropertyName("state_root_hash")] public string StateRootHash { get; init; } - + /// - /// The block timestamp. + /// The timestamp from when the block was proposed. /// - [JsonPropertyName("timestamp")] + [JsonPropertyName("timestamp")] public string Timestamp { get; init; } } + public class BlockHeaderV2 : BlockHeaderV1 + { + /// + /// The `EraEnd` of a block if it is a switch block. + /// + [JsonPropertyName("era_end")] + public EraEndV2 EraEnd { get; init; } + + /// + /// The gas price of the era. + /// + [JsonPropertyName("current_gas_price")] + public UInt16 CurrentGasPrice { get; init; } + } + /// /// Validator that proposed the block /// @@ -89,12 +167,12 @@ public bool isSystem get => this.IsSystem; set => this.IsSystem = value; } - + /// /// Validator's public key /// public PublicKey PublicKey { get; set; } - + /// /// Json converter class to serialize/deserialize a Proposer to/from Json /// @@ -134,62 +212,113 @@ public override void Write( } } } - + /// /// A block body /// - public class BlockBody + public class BlockBodyV1 { + public virtual uint Version + { + get { return 1; } + } + /// - /// List of Deploy hashes included in the block + /// The deploy hashes of the non-transfer deploys within the block. /// [JsonPropertyName("deploy_hashes")] public List DeployHashes { get; init; } - + /// /// Public key of the validator that proposed the block /// [JsonPropertyName("proposer")] [JsonConverter(typeof(Proposer.ProposerConverter))] public Proposer Proposer { get; init; } - + /// - /// List of Transfer hashes included in the block + /// The deploy hashes of the transfers within the block. /// [JsonPropertyName("transfer_hashes")] public List TransferHashes { get; init; } } /// - /// Block's finality signature. + /// A block body /// - public class BlockProof + public class BlockBodyV2 { /// - /// Validator public key + /// The hashes of the installer/upgrader transactions within the block. /// - [JsonPropertyName("public_key")] - [JsonConverter(typeof(PublicKey.PublicKeyConverter))] - public PublicKey PublicKey { get; init; } - + [JsonPropertyName("install_upgrade")] + public List InstallUpgrade { get; init; } + /// - /// Validator signature + /// The hashes of the auction transactions within the block. /// - [JsonPropertyName("signature")] - [JsonConverter(typeof(Signature.SignatureConverter))] - public Signature Signature { get; init; } + [JsonPropertyName("auction")] + public List Auction { get; init; } + + /// + /// The hashes of all other (non-installer/upgrader) transactions within the block. + /// + [JsonPropertyName("standard")] + public List Standard { get; init; } + + /// + /// Public key of the validator that proposed the block + /// + [JsonPropertyName("proposer")] + [JsonConverter(typeof(Proposer.ProposerConverter))] + public Proposer Proposer { get; init; } + + /// + /// The hashes of the mint transactions within the block. + /// + [JsonPropertyName("mint")] + public List Mint { get; init; } + + /// + /// Describes finality signatures that will be rewarded in a block. Consists of a vector of + /// `SingleBlockRewardedSignatures`, each of which describes signatures for a single ancestor block. + /// The first entry represents the signatures for the parent block, the second for the parent of the parent, + /// and so on. + /// + [JsonPropertyName("rewarded_signatures")] + public List> RewardedSignatures { get; init; } + } + + public interface IBlock + { + public int Version { get; } + public BlockV1 BlockV1 { get; } + public BlockV2 BlockV2 { get; } + + public string Hash { get; init; } } - /// - /// A block in the network - /// - public class Block + public abstract class Block : IBlock { + protected int _version; + /// - /// Block body + /// Returns the version of the block. /// - [JsonPropertyName("body")] - public BlockBody Body { get; init; } + public int Version + { + get { return _version; } + } + + /// + /// Returns the block as a Version1 block object. + /// + BlockV1 IBlock.BlockV1 => this as BlockV1; + + /// + /// Returns the block as a Version2 block object. + /// + BlockV2 IBlock.BlockV2 => this as BlockV2; /// /// Block hash @@ -197,16 +326,151 @@ public class Block [JsonPropertyName("hash")] public string Hash { get; init; } + /// + /// Json converter class to serialize/deserialize a Block to/from Json + /// + public class BlockConverter : JsonConverter + { + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert == typeof(IBlock) || + typeToConvert == typeof(Block); + } + + public override IBlock Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + reader.Read(); + var version = reader.GetString(); + reader.Read(); + switch (version) + { + case "Version1": + { + var block1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + block1._version = 1; + return block1; + } + case "Version2": + var block2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + block2._version = 2; + return block2; + default: + throw new JsonException("Expected Version1 or Version2"); + } + + ; + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + IBlock block, + JsonSerializerOptions options) + { + switch (block.Version) + { + case 1: + writer.WritePropertyName("Version1"); + writer.WriteStartObject(); + JsonSerializer.Serialize(block as BlockV1, options); + writer.WriteEndObject(); + break; + case 2: + writer.WritePropertyName("Version2"); + writer.WriteStartObject(); + JsonSerializer.Serialize(block as BlockV2, options); + writer.WriteEndObject(); + break; + default: + throw new JsonException($"Unexpected block version {block.Version}"); + } + } + } + } + + /// + /// A block in the network + /// + public class BlockV1 : Block + { + /// + /// Block header + /// + [JsonPropertyName("header")] + public BlockHeaderV1 Header { get; init; } + + /// + /// Block body + /// + [JsonPropertyName("body")] + public BlockBodyV1 Body { get; init; } + } + + /// + /// A block in the network + /// + public class BlockV2 : Block + { /// /// Block header /// [JsonPropertyName("header")] - public BlockHeader Header { get; init; } + public BlockHeaderV2 Header { get; init; } + + /// + /// Block body + /// + [JsonPropertyName("body")] + public BlockBodyV2 Body { get; init; } + } + + /// + /// A validator's public key paired with a corresponding signature of a given block hash. + /// + public class BlockProof + { + /// + /// The validator's public key. + /// + [JsonPropertyName("public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey PublicKey { get; init; } + + /// + /// The validator's signature. + /// + [JsonPropertyName("signature")] + [JsonConverter(typeof(Signature.SignatureConverter))] + public Signature Signature { get; init; } + } + + /// + /// A JSON-friendly representation of a block and the signatures for that block + /// + public class BlockWithSignatures + { + /// + /// The block. + /// + [JsonPropertyName("block")] + [JsonConverter(typeof(Block.BlockConverter))] + public IBlock Block { get; init; } /// - /// List of proofs for this block. + /// The proofs of the block, i.e. a collection of validators' signatures of the block hash. /// [JsonPropertyName("proofs")] public List Proofs { get; init; } } -} +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/EraEnd.cs b/Casper.Network.SDK/Types/EraEnd.cs index 0674da2..9a1d32b 100644 --- a/Casper.Network.SDK/Types/EraEnd.cs +++ b/Casper.Network.SDK/Types/EraEnd.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; @@ -5,18 +6,18 @@ namespace Casper.Network.SDK.Types { /// - /// A validator reward + /// validator's public key paired with a measure of the value of its contribution to consensus, as a fraction of the configured maximum block reward. /// public class Reward { /// - /// Reward amount + /// The reward amount. /// [JsonPropertyName("amount")] public ulong Amount { get; init; } /// - /// Validator public key + /// The validator's public key. /// [JsonPropertyName("validator")] [JsonConverter(typeof(PublicKey.PublicKeyConverter))] @@ -24,38 +25,38 @@ public class Reward } /// - /// Equivocation and reward information to be included in the terminal block. + /// Equivocation, reward and validator inactivity information. /// public class EraReport { /// - /// List of public keys of the equivocators + /// The set of equivocators. /// [JsonPropertyName("equivocators")] [JsonConverter(typeof(GenericListConverter))] public List Equivocators { get; init; } /// - /// List of public keys of inactive validators + /// Validators that haven't produced any unit during the era. /// [JsonPropertyName("inactive_validators")] [JsonConverter(typeof(GenericListConverter))] public List InactiveValidators { get; init; } /// - /// List of validators with rewards + /// Rewards for finalization of earlier blocks. /// [JsonPropertyName("rewards")] public List Rewards { get; init; } } /// - /// Era end report and list of validator weights for next era + /// Information related to the end of an era, and validator weights for the following era. /// - public class EraEnd + public class EraEndV1 { /// - /// Era report + /// Equivocation, reward and validator inactivity information. /// [JsonPropertyName("era_report")] public EraReport EraReport { get; init; } @@ -67,4 +68,44 @@ public class EraEnd [JsonConverter(typeof(GenericListConverter))] public List NextEraValidatorWeights { get; init; } } + + + /// + /// Information related to the end of an era, and validator weights for the following era. + /// + public class EraEndV2 + { + /// + /// The set of equivocators. + /// + [JsonPropertyName("equivocators")] + [JsonConverter(typeof(GenericListConverter))] + public List Equivocators { get; init; } + + /// + /// Validators that haven't produced any unit during the era. + /// + [JsonPropertyName("inactive_validators")] + [JsonConverter(typeof(GenericListConverter))] + public List InactiveValidators { get; init; } + + /// + /// A list of validator weights for the next era + /// + [JsonPropertyName("next_era_validator_weights")] + [JsonConverter(typeof(GenericListConverter))] + public List NextEraValidatorWeights { get; init; } + + /// + /// The rewards distributed to the validators. + /// + [JsonPropertyName("rewards")] + public Dictionary Rewards { get; init; } + + /// + /// Next Era gas price + /// + [JsonPropertyName("next_era_gas_price")] + public UInt16 NextEraGasPrice { get; init; } + } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/InitiatorAddr.cs b/Casper.Network.SDK/Types/InitiatorAddr.cs new file mode 100644 index 0000000..6a1e87d --- /dev/null +++ b/Casper.Network.SDK/Types/InitiatorAddr.cs @@ -0,0 +1,56 @@ +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + /// + /// The address of the initiator of a TransactionV1 + /// + public class InitiatorAddr + { + /// + /// The public key of the initiator + /// + [JsonPropertyName("PublicKey")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey PublicKey { get; init; } + + /// + /// The account hash derived from the public key of the initiator + /// + [JsonPropertyName("AccountHash")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public AccountHashKey AccountHash { get; init; } + + public InitiatorAddr() + { + } + + public InitiatorAddr(PublicKey publicKey) + { + this.PublicKey = publicKey; + } + + public InitiatorAddr(AccountHashKey accountHash) + { + this.AccountHash = accountHash; + } + + public override string ToString() + { + return PublicKey != null + ? PublicKey.ToString() + : (AccountHash != null + ? AccountHash.ToString() + : null); + } + + public string ToHexString() + { + return PublicKey != null + ? PublicKey.ToString() + : (AccountHash != null + ? AccountHash.ToHexString() + : null); + } + } +} diff --git a/Casper.Network.SDK/Types/TransactionHash.cs b/Casper.Network.SDK/Types/TransactionHash.cs new file mode 100644 index 0000000..7b5e26b --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionHash.cs @@ -0,0 +1,14 @@ +namespace Casper.Network.SDK.Types +{ + public class TransactionHash + { + public string Deploy { get; init; } + + public string Version1 { get; init; } + + public override string ToString() + { + return Deploy ?? Version1; + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Transfer.cs b/Casper.Network.SDK/Types/Transfer.cs index ef892d8..c6cf43b 100644 --- a/Casper.Network.SDK/Types/Transfer.cs +++ b/Casper.Network.SDK/Types/Transfer.cs @@ -1,4 +1,6 @@ +using System; using System.Numerics; +using System.Text.Json; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; @@ -7,7 +9,7 @@ namespace Casper.Network.SDK.Types /// /// Represents a transfer from one purse to another /// - public class Transfer + public class TransferV1 : Transfer { /// /// Transfer amount @@ -63,4 +65,165 @@ public class Transfer [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public AccountHashKey To { get; init; } } + + /// + /// Represents a version 2 transfer from one purse to another + /// + public class TransferV2 : Transfer + { + /// + /// Transfer amount + /// + [JsonPropertyName("amount")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Amount { get; init; } + + /// + /// Transaction that created the transfer + /// + [JsonPropertyName("transaction_hash")] + public TransactionHash TransactionHash { get; init; } + + /// + /// Entity from which transfer was executed + /// + [JsonPropertyName("from")] + public InitiatorAddr From { get; init; } + + /// + /// Gas + /// + [JsonPropertyName("gas")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Gas { get; init; } + + /// + /// User-defined id + /// + [JsonPropertyName("id")] + public ulong? Id { get; init; } + + /// + /// Source purse + /// + [JsonPropertyName("source")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public URef Source { get; init; } + + /// + /// Target purse + /// + [JsonPropertyName("target")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public URef Target { get; init; } + + /// + /// Account to which funds are transferred + /// + [JsonPropertyName("to")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public AccountHashKey To { get; init; } + } + + public interface ITransfer + { + public int Version { get; } + public TransferV1 TransferV1 { get; } + public TransferV2 TransferV2 { get; } + } + + public class Transfer: ITransfer + { + protected int _version; + + /// + /// Returns the version of the transfer. + /// + public int Version + { + get { return _version; } + } + + /// + /// Returns the transfer as a Version1 transfer object. + /// + TransferV1 ITransfer.TransferV1 => this as TransferV1; + + /// + /// Returns the transfer as a Version2 transfer object. + /// + TransferV2 ITransfer.TransferV2 => this as TransferV2; + + /// + /// Json converter class to serialize/deserialize a Block to/from Json + /// + public class TransferConverter : JsonConverter + { + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert == typeof(ITransfer) || + typeToConvert == typeof(Transfer); + } + + public override ITransfer Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + reader.Read(); + var version = reader.GetString(); + reader.Read(); + switch (version) + { + case "Version1": + { + var transfer1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + transfer1._version = 1; + return transfer1; + } + case "Version2": + var transfer2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + transfer2._version = 2; + return transfer2; + default: + throw new JsonException("Expected Version1 or Version2"); + } + + ; + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + ITransfer transfer, + JsonSerializerOptions options) + { + switch (transfer.Version) + { + case 1: + writer.WritePropertyName("Version1"); + writer.WriteStartObject(); + JsonSerializer.Serialize(transfer as TransferV1, options); + writer.WriteEndObject(); + break; + case 2: + writer.WritePropertyName("Version2"); + writer.WriteStartObject(); + JsonSerializer.Serialize(transfer as TransferV2, options); + writer.WriteEndObject(); + break; + default: + throw new JsonException($"Unexpected transfer version {transfer.Version}"); + } + } + } + } } \ No newline at end of file From 785f6fac0efdb1f6d55375454e541d00fa762487 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 16 May 2024 13:01:13 +0200 Subject: [PATCH 008/126] CSDK-116 new global state keys in Casper2 Signed-off-by: David Hernando --- Casper.Network.SDK.Test/GlobalStateKeyTest.cs | 151 ++++++++++++++++++ .../ResultTypes/QueryGlobalStateResult.cs | 3 +- .../GlobalStateKey/AddressableEntityKey.cs | 89 +++++++++++ .../Types/GlobalStateKey/BalanceHoldKey.cs | 63 ++++++++ .../Types/GlobalStateKey/BidAddrKey.cs | 81 ++++++++++ .../GlobalStateKey/BlockGlobalAddrKey.cs | 70 ++++++++ .../Types/GlobalStateKey/ByteCodeKey.cs | 65 ++++++++ .../{ => GlobalStateKey}/GlobalStateKey.cs | 70 +++++++- .../Types/GlobalStateKey/MessageKey.cs | 88 ++++++++++ .../Types/GlobalStateKey/NamedKeyKey.cs | 44 +++++ .../Types/GlobalStateKey/PackageKey.cs | 18 +++ 11 files changed, 737 insertions(+), 5 deletions(-) create mode 100644 Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs create mode 100644 Casper.Network.SDK/Types/GlobalStateKey/BalanceHoldKey.cs create mode 100644 Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs create mode 100644 Casper.Network.SDK/Types/GlobalStateKey/BlockGlobalAddrKey.cs create mode 100644 Casper.Network.SDK/Types/GlobalStateKey/ByteCodeKey.cs rename Casper.Network.SDK/Types/{ => GlobalStateKey}/GlobalStateKey.cs (87%) create mode 100644 Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs create mode 100644 Casper.Network.SDK/Types/GlobalStateKey/NamedKeyKey.cs create mode 100644 Casper.Network.SDK/Types/GlobalStateKey/PackageKey.cs diff --git a/Casper.Network.SDK.Test/GlobalStateKeyTest.cs b/Casper.Network.SDK.Test/GlobalStateKeyTest.cs index 6a9ed65..2e99f18 100644 --- a/Casper.Network.SDK.Test/GlobalStateKeyTest.cs +++ b/Casper.Network.SDK.Test/GlobalStateKeyTest.cs @@ -1,7 +1,9 @@ using System; using System.Linq; +using System.Runtime.InteropServices.JavaScript; using System.Text.Json; using Casper.Network.SDK.Types; +using Casper.Network.SDK.Utils; using NUnit.Framework; using Org.BouncyCastle.Utilities.Encoders; @@ -457,5 +459,154 @@ public void HashKeyJsonDeserializeTest() Assert.IsNotNull(ex); Assert.IsTrue(ex.Message.Contains("checksum mismatch")); } + + [Test] + public void AddressableEntityKeyTest() + { + const string ENTITY_PREFIX = "entity-"; + + var entityAddr = "0101010101010101010101010101010101010101010101010101010101010101"; + + var entities = new EntityKind[] { EntityKind.System, EntityKind.Account, EntityKind.Contract }; + foreach (var entityKind in entities) + { + var entityKey = $"{ENTITY_PREFIX}{entityKind.ToString().ToLower()}-{entityAddr}"; + + var key = GlobalStateKey.FromString(entityKey) as AddressableEntityKey; + Assert.IsNotNull(key); + Assert.AreEqual(KeyIdentifier.AddressableEntity, key.KeyIdentifier); + + Assert.AreEqual(entityKind, key.Kind); + Assert.AreEqual($"{ENTITY_PREFIX}{entityKind.ToString().ToLower()}-"+entityAddr, key.ToString().ToLower()); + } + } + + [Test] + public void ByteCodeKeyTest() + { + var addr = "0101010101010101010101010101010101010101010101010101010101010101"; + + var kinds = new ByteCodeKind[] { ByteCodeKind.Empty, ByteCodeKind.V1CasperWasm }; + foreach (var kind in kinds) + { + var keyStr = $"{kind.ToKeyPrefix()}{addr}"; + + var key = GlobalStateKey.FromString(keyStr); + Assert.IsNotNull(key); + Assert.AreEqual(KeyIdentifier.ByteCode, key.KeyIdentifier); + + var byteCodeKey = key as ByteCodeKey; + Assert.IsNotNull(byteCodeKey); + + Assert.AreEqual(kind, byteCodeKey.Kind); + Assert.AreEqual(keyStr, key.ToString().ToLower()); + Assert.AreEqual(keyStr, byteCodeKey.ToString().ToLower()); + } + } + + [Test] + public void NamedKeyKeyTest() + { + const string NAMEDKEY_PREFIX = "named-key-"; + const string ENTITY_PREFIX = "entity-"; + + var entityAddr = "0101010101010101010101010101010101010101010101010101010101010101"; + var namedKeyAddr = "0202020202020202020202020202020202020202020202020202020202020202"; + + var entities = new EntityKind[] { EntityKind.System, EntityKind.Account, EntityKind.Contract }; + foreach (var entityKind in entities) + { + var namedKeyKey = $"{NAMEDKEY_PREFIX}{ENTITY_PREFIX}{entityKind.ToString().ToLower()}-{entityAddr}-{namedKeyAddr}"; + + var key = GlobalStateKey.FromString(namedKeyKey); + Assert.IsNotNull(key); + Assert.AreEqual(KeyIdentifier.NamedKey, key.KeyIdentifier); + + var namedKey = key as NamedKeyKey; + Assert.IsNotNull(namedKey); + + var entity = namedKey.AddressableEntity; + Assert.IsNotNull(entity); + Assert.AreEqual(KeyIdentifier.AddressableEntity, entity.KeyIdentifier); + Assert.AreEqual(entityKind, entity.Kind); + Assert.AreEqual($"{ENTITY_PREFIX}{entityKind.ToString().ToLower()}-"+entityAddr, entity.ToString().ToLower()); + } + } + + [Test] + public void BalanceHoldKeyTest() + { + const string NAMEDKEY_PREFIX = "balance-hold-"; + + var tags = new BalanceHoldTag[] { BalanceHoldTag.Gas, BalanceHoldTag.Processing }; + + var uref = "0101010101010101010101010101010101010101010101010101010101010101"; + UInt64 blockTime = DateUtils.ToEpochTime(DateTime.Now); + + foreach (var tag in tags) + { + byte[] timeBytes = BitConverter.GetBytes(blockTime); + if (!BitConverter.IsLittleEndian) + Array.Reverse(timeBytes); + + var keyStr = + $"{NAMEDKEY_PREFIX}{Hex.ToHexString(new byte[] {(byte)tag})}{uref}{Hex.ToHexString(timeBytes)}"; + + var key = GlobalStateKey.FromString(keyStr); + Assert.IsNotNull(key); + Assert.AreEqual(KeyIdentifier.BalanceHold, key.KeyIdentifier); + + var balanceHoldKey = key as BalanceHoldKey; + Assert.IsNotNull(balanceHoldKey); + Assert.AreEqual(tag, balanceHoldKey.Tag); + Assert.AreEqual(blockTime, balanceHoldKey.BlockTime); + Assert.AreEqual($"uref-{uref}-000", balanceHoldKey.URef.ToString().ToLower()); + Assert.AreEqual(keyStr, balanceHoldKey.ToString().ToLower()); + } + } + + [Test] + public void MessageTopicKeyTest() + { + var entityKeyStr = "entity-contract-55d4a6915291da12afded37fa5bc01f0803a2f0faf6acb7ec4c7ca6ab76f3330"; + var topicStr = "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1"; + var msgKeyStr = $"message-topic-{entityKeyStr}-{topicStr}"; + + var key = GlobalStateKey.FromString(msgKeyStr); + Assert.IsNotNull(key); + Assert.AreEqual(KeyIdentifier.Message, key.KeyIdentifier); + + var messageKey = key as MessageKey; + Assert.IsNotNull(messageKey); + Assert.IsNotNull(messageKey.AddressableEntity); + Assert.AreEqual(entityKeyStr, messageKey.AddressableEntity.ToString().ToLower()); + Assert.AreEqual(topicStr, messageKey.TopicHash); + Assert.IsFalse(messageKey.Index.HasValue); + Assert.AreEqual(msgKeyStr, messageKey.ToString().ToLower()); + Assert.AreEqual(msgKeyStr, key.ToString().ToLower()); + } + + [Test] + public void MessageIndexKeyTest() + { + var entityKeyStr = "entity-contract-55d4a6915291da12afded37fa5bc01f0803a2f0faf6acb7ec4c7ca6ab76f3330"; + var topicStr = "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1"; + var indexStr = "f"; + var msgKeyStr = $"message-{entityKeyStr}-{topicStr}-{indexStr}"; + + var key = GlobalStateKey.FromString(msgKeyStr); + Assert.IsNotNull(key); + Assert.AreEqual(KeyIdentifier.Message, key.KeyIdentifier); + + var messageKey = key as MessageKey; + Assert.IsNotNull(messageKey); + Assert.IsNotNull(messageKey.AddressableEntity); + Assert.AreEqual(entityKeyStr, messageKey.AddressableEntity.ToString().ToLower()); + Assert.AreEqual(topicStr, messageKey.TopicHash); + Assert.IsTrue(messageKey.Index.HasValue); + Assert.AreEqual(15, messageKey.Index.Value); + Assert.AreEqual(msgKeyStr, messageKey.ToString().ToLower()); + Assert.AreEqual(msgKeyStr, key.ToString().ToLower()); + } } } diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs index 3b02e40..b5c5710 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs @@ -12,7 +12,8 @@ public class QueryGlobalStateResult : RpcResult /// The block header if a Block hash was provided. /// [JsonPropertyName("block_header")] - public BlockHeader BlockHeader { get; init; } + [JsonConverter(typeof(BlockHeader.BlockHeaderConverter))] + public BlockHeaderV1 BlockHeader { get; init; } /// /// The stored value. diff --git a/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs new file mode 100644 index 0000000..063101d --- /dev/null +++ b/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs @@ -0,0 +1,89 @@ +using System; +using System.IO; +using Casper.Network.SDK.Utils; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Casper.Network.SDK.Types +{ + public enum EntityKind { + /// + /// System variant. + /// + System = 0, + /// + /// Account variant. + /// + Account = 1, + /// + /// SmartContract variant. + /// + Contract = 2, + } + + public static class EntityKindExtensions + { + public static string ToKeyPrefix(this EntityKind kind) + { + switch (kind) + { + case EntityKind.System: + return "entity-system-"; + case EntityKind.Account: + return "entity-account-"; + case EntityKind.Contract: + return "entity-contract-"; + default: + return kind.ToString(); + } + } + } + + public class AddressableEntityKey : GlobalStateKey + { + public EntityKind Kind { get; init; } + + private static string GetPrefix(string key) + { + if (key.StartsWith(EntityKind.System.ToKeyPrefix())) + return EntityKind.System.ToKeyPrefix(); + if (key.StartsWith(EntityKind.Account.ToKeyPrefix())) + return EntityKind.Account.ToKeyPrefix(); + if (key.StartsWith(EntityKind.Contract.ToKeyPrefix())) + return EntityKind.Contract.ToKeyPrefix(); + + throw new Exception("Unexpected key prefix in NamedKeyKey: " + key); + + } + public AddressableEntityKey(string key) : base(key, GetPrefix(key)) + { + KeyIdentifier = KeyIdentifier.AddressableEntity; + var prefix = GetPrefix(key); + if (EntityKind.System.ToKeyPrefix().Equals(prefix)) + Kind = EntityKind.System; + else if (EntityKind.Account.ToKeyPrefix().Equals(prefix)) + Kind = EntityKind.Account; + else if (EntityKind.Contract.ToKeyPrefix().Equals(prefix)) + Kind = EntityKind.Contract; + } + + public AddressableEntityKey(byte[] key) : this( + key[0] == (byte)EntityKind.System + ? EntityKind.System.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + : (key[0] == (byte)EntityKind.Account + ? EntityKind.Account.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + : (key[0] == (byte)EntityKind.Account + ? EntityKind.Contract.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + : throw new Exception($"Wrong entity tag '{key[0]}' for AddressableEntityKey.")))) + { + } + + public AddressableEntityKey(BinaryReader reader) : base(null) + { + KeyIdentifier = KeyIdentifier.AddressableEntity; + var tag = reader.ReadByte(); + var addr = reader.ReadBytes(32); + Kind = (EntityKind)tag; + Key = Kind.ToKeyPrefix() + Hex.ToHexString(addr); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey/BalanceHoldKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/BalanceHoldKey.cs new file mode 100644 index 0000000..b675eff --- /dev/null +++ b/Casper.Network.SDK/Types/GlobalStateKey/BalanceHoldKey.cs @@ -0,0 +1,63 @@ +using System; +using Casper.Network.SDK.Utils; + +namespace Casper.Network.SDK.Types +{ + public enum BalanceHoldTag + { + /// + /// Tag for gas variant. + /// + Gas = 0, + /// + /// Tag for processing variant. + /// + Processing = 1, + } + + public class BalanceHoldKey : GlobalStateKey + { + private const string KEYPREFIX = "balance-hold-"; + public static string KeyPrefix => KEYPREFIX; + + public BalanceHoldTag Tag { get; init; } + + public URef URef => new URef(RawBytes.Slice(1,33), AccessRights.NONE); + + public UInt64 BlockTime + { + get + { + var bytes = RawBytes.Slice(33); + if (!BitConverter.IsLittleEndian) + Array.Reverse(bytes); + return BitConverterExtensions.ToUInt64(bytes); + } + } + + public BalanceHoldKey(string key) : base(key, KEYPREFIX) + { + KeyIdentifier = KeyIdentifier.BalanceHold; + if (RawBytes.Length <= 0) + throw new Exception("Wrong key length."); + switch (RawBytes[0]) + { + case (byte)BalanceHoldTag.Gas: + Tag = BalanceHoldTag.Gas; + break; + case (byte)BalanceHoldTag.Processing: + Tag = BalanceHoldTag.Processing; + break; + default: + throw new Exception($"Wrong BalanceHold tag '{RawBytes[0]}'."); + } + + if (RawBytes.Length != 41) // tag + purse uref (32) + block time (8) + throw new Exception("Wrong key length for BalanceHold. Expected 41 bytes."); + } + + public BalanceHoldKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + { + } + } +} diff --git a/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs new file mode 100644 index 0000000..0ab3d81 --- /dev/null +++ b/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs @@ -0,0 +1,81 @@ +using System; +using Casper.Network.SDK.Utils; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Casper.Network.SDK.Types +{ + public enum BidAddrTag + { + /// + /// Unified BidAddr (refers to validator address). + /// + Unified = 0, //legacy type + /// + /// Validator BidAddr. + /// + Validator = 1, + /// + /// Delegator BidAddr, + /// + Delegator = 2, + } + + public class BidAddrKey : GlobalStateKey + { + public static string KEYPREFIX = "bid-addr-"; + + public BidAddrTag Tag { get; init; } + + public AccountHashKey Validator + { + get + { + var hash = Tag == BidAddrTag.Delegator ? Key.Substring(0,32) : Key; + return new AccountHashKey("account-hash-" + hash); + } + } + + public AccountHashKey Delegator + { + get + { + var hash = Tag == BidAddrTag.Delegator ? Key.Substring(32) : null; + return hash != null + ? new AccountHashKey("account-hash-" + hash) + : null; + } + } + + public BidAddrKey(string key) : base(key, KEYPREFIX) + { + KeyIdentifier = KeyIdentifier.BidAddr; + var bytes = Hex.Decode(Key); + if (bytes.Length <= 0) + throw new Exception("Wrong key length."); + switch (bytes[0]) + { + case (byte)BidAddrTag.Unified: + Tag = BidAddrTag.Unified; + if (bytes.Length != 33) + throw new Exception("Wrong key length for Unified BidAddr. Expected 33 bytes."); + break; + case (byte)BidAddrTag.Validator: + Tag = BidAddrTag.Validator; + if (bytes.Length != 33) + throw new Exception("Wrong key length for Validator BidAddr. Expected 33 bytes."); + break; + case (byte)BidAddrTag.Delegator: + Tag = BidAddrTag.Delegator; + if (bytes.Length != 65) + throw new Exception("Wrong key length for Unified BidAddr. Expected 65 bytes."); + break; + default: + throw new Exception($"Wrong BidAddr tag '{bytes[0]}'."); + } + } + + public BidAddrKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + { + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey/BlockGlobalAddrKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/BlockGlobalAddrKey.cs new file mode 100644 index 0000000..55ea4eb --- /dev/null +++ b/Casper.Network.SDK/Types/GlobalStateKey/BlockGlobalAddrKey.cs @@ -0,0 +1,70 @@ +using System; +using Casper.Network.SDK.Utils; + +namespace Casper.Network.SDK.Types +{ + public enum BlockGlobalAddrTag + { + /// + /// Tag for block time variant. + /// + Time = 0, + /// + /// Tag for processing variant. + /// + MessageCount = 1, + } + + public static class BlockGlobalAddrTagExtensions + { + public static string ToKeyPrefix(this BlockGlobalAddrTag tag) + { + switch (tag) + { + case BlockGlobalAddrTag.Time: + return "block-time-"; + case BlockGlobalAddrTag.MessageCount: + return "block-message-count-"; + default: + return tag.ToString(); + } + } + } + + /// + /// Address for singleton values associated to specific block. These are values which are + /// calculated or set during the execution of a block such as the block timestamp, or the + /// total count of messages emitted during the execution of the block, and so on. + /// + public class BlockGlobalAddrKey : GlobalStateKey + { + public BlockGlobalAddrTag Tag { get; init; } + + private static string GetPrefix(string key) + { + if (key.StartsWith(BlockGlobalAddrTag.Time.ToKeyPrefix())) + return BlockGlobalAddrTag.Time.ToKeyPrefix(); + if (key.StartsWith(BlockGlobalAddrTag.MessageCount.ToKeyPrefix())) + return BlockGlobalAddrTag.MessageCount.ToKeyPrefix(); + + throw new Exception("Unexpected key prefix in NamedKeyKey: " + key); + } + + public BlockGlobalAddrKey(string key) : base(key, GetPrefix(key)) + { + KeyIdentifier = KeyIdentifier.BlockGlobal; + Tag = GetPrefix(key) == BlockGlobalAddrTag.Time.ToKeyPrefix() + ? BlockGlobalAddrTag.Time + : BlockGlobalAddrTag.MessageCount; + } + + public BlockGlobalAddrKey(byte[] key) : this( + key[0] == (byte)BlockGlobalAddrTag.Time + ? BlockGlobalAddrTag.Time.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + : (key[0] == (byte)BlockGlobalAddrTag.MessageCount + ? BlockGlobalAddrTag.MessageCount.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + : throw new Exception($"Wrong kind tag '{key[0]}' for BlockGlobalAddrKey."))) + { + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey/ByteCodeKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/ByteCodeKey.cs new file mode 100644 index 0000000..7acc9aa --- /dev/null +++ b/Casper.Network.SDK/Types/GlobalStateKey/ByteCodeKey.cs @@ -0,0 +1,65 @@ +using System; +using Casper.Network.SDK.Utils; + +namespace Casper.Network.SDK.Types +{ + public enum ByteCodeKind { + /// + /// Empty byte code. + /// + Empty = 0, + /// + /// Byte code to be executed with the version 1 Casper execution engine. + /// + V1CasperWasm = 1, + } + + public static class ByteCodeKindExtensions + { + public static string ToKeyPrefix(this ByteCodeKind kind) + { + switch (kind) + { + case ByteCodeKind.Empty: + return "byte-code-empty-"; + case ByteCodeKind.V1CasperWasm: + return "byte-code-v1-wasm-"; + default: + return kind.ToString(); + } + } + } + + public class ByteCodeKey : GlobalStateKey + { + public ByteCodeKind Kind { get; init; } + + private static string GetPrefix(string key) + { + if (key.StartsWith(ByteCodeKind.Empty.ToKeyPrefix())) + return ByteCodeKind.Empty.ToKeyPrefix(); + if (key.StartsWith(ByteCodeKind.V1CasperWasm.ToKeyPrefix())) + return ByteCodeKind.V1CasperWasm.ToKeyPrefix(); + throw new Exception("Unexpected key prefix in ByteCodeKey: " + key); + } + + public ByteCodeKey(string key) : base(key, ByteCodeKey.GetPrefix(key)) + { + KeyIdentifier = KeyIdentifier.ByteCode; + var prefix = GetPrefix(key); + if (ByteCodeKind.Empty.ToKeyPrefix().Equals(prefix)) + Kind = ByteCodeKind.Empty; + else if (ByteCodeKind.V1CasperWasm.ToKeyPrefix().Equals(prefix)) + Kind = ByteCodeKind.V1CasperWasm; + } + + public ByteCodeKey(byte[] key) : this( + key[0] == (byte)ByteCodeKind.Empty + ? ByteCodeKind.Empty.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + : (key[0] == (byte)ByteCodeKind.V1CasperWasm + ? ByteCodeKind.V1CasperWasm.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + : throw new Exception($"Wrong kind tag '{key[0]}' for ByteCodeKey."))) + { + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs similarity index 87% rename from Casper.Network.SDK/Types/GlobalStateKey.cs rename to Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs index aef55ce..cc7f62e 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs @@ -74,14 +74,43 @@ public enum KeyIdentifier /// A `Key` variant under which a registry of checksums are stored. /// ChecksumRegistry = 0x0e, + /// + /// A `Key` under which bid information is stored. + /// + BidAddr = 0x0f, + /// + /// A `Key` under which package information is stored. + /// + Package = 0x10, + /// + /// A `Key` under which an addressable entity is stored. + /// + AddressableEntity = 0x11, + /// + /// A `Key` under which a byte code record is stored. + /// + ByteCode = 0x12, + /// + /// A `Key` under which a message is stored. + /// + Message = 0x13, + /// + /// A `Key` under which a single named key entry is stored. + /// + NamedKey = 0x14, + BlockGlobal = 0x15, + /// + /// A `Key` under which a hold on a purse balance is stored. + /// + BalanceHold = 0x16, } - + /// /// Base class for the different global state keys. /// public abstract class GlobalStateKey { - protected readonly string Key; + protected string Key { get; init; } public KeyIdentifier KeyIdentifier { get; init; } @@ -139,8 +168,12 @@ public static GlobalStateKey FromString(string value) return new TransferKey(value); if (value.StartsWith("deploy-")) return new DeployInfoKey(value); + if (value.StartsWith("balance-hold-")) + return new BalanceHoldKey(value); if (value.StartsWith("balance-")) return new BalanceKey(value); + if (value.StartsWith("bid-addr-")) + return new BidAddrKey(value); if (value.StartsWith("bid")) return new BidKey(value); if (value.StartsWith("withdraw")) @@ -159,7 +192,20 @@ public static GlobalStateKey FromString(string value) return new ChainspecRegistryKey(value); if (value.StartsWith("checksum-registry-")) return new ChecksumRegistryKey(value); - + if (value.StartsWith("package-")) + return new PackageKey(value); + if (value.StartsWith("block-message-count-")) + return new BlockGlobalAddrKey(value); + if (value.StartsWith("block-time-")) + return new BlockGlobalAddrKey(value); + if (value.StartsWith("entity-")) + return new AddressableEntityKey(value); + if (value.StartsWith("named-key-")) + return new NamedKeyKey(value); + if (value.StartsWith("byte-code-")) + return new ByteCodeKey(value); + if (value.StartsWith("message-")) + return new MessageKey(value); throw new ArgumentException($"Key not valid. Unknown key prefix in \"{value}\"."); } @@ -186,6 +232,14 @@ public static GlobalStateKey FromBytes(byte[] bytes) 0x0c => new UnbondKey("unbond-" + CEP57Checksum.Encode(bytes.Slice(1))), 0x0d => new ChainspecRegistryKey("chainspec-registry-" + CEP57Checksum.Encode(bytes.Slice(1))), 0x0e => new ChecksumRegistryKey("checksum-registry-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x0f => new BidAddrKey("bid-addr-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x10 => new PackageKey("package-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x11 => new AddressableEntityKey(bytes.Slice(1)), + 0x12 => new ByteCodeKey(bytes.Slice(1)), + 0x13 => new MessageKey(bytes.Slice(1)), + 0x14 => new NamedKeyKey(bytes.Slice(1)), + 0x15 => new BlockGlobalAddrKey(bytes.Slice(1)), + 0x16 => new BalanceHoldKey(bytes.Slice(1)), _ => throw new ArgumentException($"Unknown key identifier '{bytes[0]}'") }; } @@ -230,7 +284,15 @@ public override bool CanConvert(Type typeToConvert) typeToConvert == typeof(EraSummaryKey) || typeToConvert == typeof(UnbondKey) || typeToConvert == typeof(ChainspecRegistryKey) || - typeToConvert == typeof(ChecksumRegistryKey); + typeToConvert == typeof(ChecksumRegistryKey) || + typeToConvert == typeof(BidAddrKey) || + typeToConvert == typeof(PackageKey) || + typeToConvert == typeof(AddressableEntityKey) || + typeToConvert == typeof(ByteCodeKey) || + typeToConvert == typeof(MessageKey) || + typeToConvert == typeof(NamedKeyKey) || + typeToConvert == typeof(BlockGlobalAddrKey) || + typeToConvert == typeof(BalanceHoldKey); } public override JsonConverter CreateConverter( diff --git a/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs new file mode 100644 index 0000000..077696c --- /dev/null +++ b/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs @@ -0,0 +1,88 @@ +using System; +using System.IO; +using Casper.Network.SDK.Utils; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Casper.Network.SDK.Types +{ + public class MessageKey : GlobalStateKey + { + private const string MESSAGE_PREFIX = "message-"; + private const string TOPIC_PREFIX = "topic-"; + + private static readonly string CONTRACT_TOPIC_KEYPREFIX = "message-topic-entity-contract-"; + private static readonly string CONTRACT_MSG_KEYPREFIX = "message-entity-contract-"; + + public AddressableEntityKey AddressableEntity { get; init; } + + public string TopicHash { get; init; } + + public UInt32? Index { get; init; } + + private static string GetPrefix(string key) + { + if (key.StartsWith(CONTRACT_TOPIC_KEYPREFIX)) + return CONTRACT_TOPIC_KEYPREFIX; + if (key.StartsWith(CONTRACT_MSG_KEYPREFIX)) + return CONTRACT_MSG_KEYPREFIX; + + throw new Exception("Unexpected key prefix in ByteCodeKey: " + key); + } + + public MessageKey(string key) : base(key) + { + KeyIdentifier = KeyIdentifier.Message; + + if (!key.StartsWith(MESSAGE_PREFIX)) + throw new ArgumentException($"Key not valid. It should start with '{MESSAGE_PREFIX}'."); + key = key.Substring(MESSAGE_PREFIX.Length); + + if (key.StartsWith(TOPIC_PREFIX)) + { + key = key.Substring(TOPIC_PREFIX.Length); + var parts = key.Split('-'); + if (parts.Length != 4) + throw new Exception("Key not valid. It should have an entity address and a topic hash."); + + AddressableEntity = new AddressableEntityKey($"{parts[0]}-{parts[1]}-{parts[2]}"); + TopicHash = parts[3]; + } + else + { + var parts = key.Split('-'); + if (parts.Length != 5) + throw new Exception("Key not valid. It should have an entity address, a topic hash, and a message index."); + + AddressableEntity = new AddressableEntityKey($"{parts[0]}-{parts[1]}-{parts[2]}"); + TopicHash = parts[3]; + + if(parts[4].Length == 0) + throw new Exception("Key not valid. Expected a non-empty message index."); + Index = Convert.ToUInt32(parts[4], 16); + } + } + + public MessageKey(byte[] key) : base(null) + { + KeyIdentifier = KeyIdentifier.Message; + + var ms = new MemoryStream(key); + var reader = new BinaryReader(ms); + + AddressableEntity = new AddressableEntityKey(reader); + + var topic = reader.ReadBytes(32); + TopicHash = Hex.ToHexString(topic); + + var optionIndexTag = reader.ReadByte(); + if (optionIndexTag == 0x01) + Index = reader.ReadUInt32(); + + Key = MESSAGE_PREFIX + + (Index.HasValue ? "" : TOPIC_PREFIX) + + AddressableEntity.ToString() + "-" + + TopicHash + + (Index.HasValue ? "-" + Index.Value.ToString("x") : ""); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey/NamedKeyKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/NamedKeyKey.cs new file mode 100644 index 0000000..b26afa3 --- /dev/null +++ b/Casper.Network.SDK/Types/GlobalStateKey/NamedKeyKey.cs @@ -0,0 +1,44 @@ +using System; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Casper.Network.SDK.Types +{ + public class NamedKeyKey : GlobalStateKey + { + private const string NAMEDKEYPREFIX = "named-key-"; + + public AddressableEntityKey AddressableEntity { get; init; } + + private static string GetPrefix(string key) + { + if (key.StartsWith(NAMEDKEYPREFIX+EntityKind.System.ToKeyPrefix())) + return NAMEDKEYPREFIX+EntityKind.System.ToKeyPrefix(); + if (key.StartsWith(NAMEDKEYPREFIX+EntityKind.Account.ToKeyPrefix())) + return NAMEDKEYPREFIX+EntityKind.Account.ToKeyPrefix(); + if (key.StartsWith(NAMEDKEYPREFIX+EntityKind.Contract.ToKeyPrefix())) + return NAMEDKEYPREFIX+EntityKind.Contract.ToKeyPrefix(); + + throw new Exception("Unexpected key prefix in NamedKeyKey: " + key); + } + + public NamedKeyKey(string key) : base(key, NamedKeyKey.GetPrefix(key)) + { + KeyIdentifier = KeyIdentifier.NamedKey; + + var subkey = key.Substring(NAMEDKEYPREFIX.Length); + subkey = subkey.Remove(subkey.LastIndexOf('-')); + AddressableEntity = new AddressableEntityKey(subkey); + } + + private static string _getString(byte[] key) + { + var entityAddress = new AddressableEntityKey(key.Slice(0, 33)); + var addr = Hex.ToHexString(key.Slice(33)); + return $"{NAMEDKEYPREFIX}{entityAddress}-{addr}"; + } + + public NamedKeyKey(byte[] key) : this(_getString(key)) + { + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey/PackageKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/PackageKey.cs new file mode 100644 index 0000000..fa4d124 --- /dev/null +++ b/Casper.Network.SDK/Types/GlobalStateKey/PackageKey.cs @@ -0,0 +1,18 @@ +using Casper.Network.SDK.Utils; + +namespace Casper.Network.SDK.Types +{ + public class PackageKey : GlobalStateKey + { + public static string KEYPREFIX = "package-"; + + public PackageKey(string key) : base(key, KEYPREFIX) + { + KeyIdentifier = KeyIdentifier.Package; + } + + public PackageKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + { + } + } +} \ No newline at end of file From b04cae8a5c66786edc7910c207b3d8bb35c995fd Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 16 May 2024 17:03:37 +0200 Subject: [PATCH 009/126] CSDK-119 state_get_entity implementation Signed-off-by: David Hernando --- Casper.Network.SDK.Test/GlobalStateKeyTest.cs | 4 +- Casper.Network.SDK/JsonRpc/CasperMethods.cs | 31 ++- .../JsonRpc/ResultTypes/GetEntityResult.cs | 105 ++++++++++ Casper.Network.SDK/NetCasperClient.cs | 52 ++++- Casper.Network.SDK/Types/Account.cs | 6 + Casper.Network.SDK/Types/AddressableEntity.cs | 186 ++++++++++++++++++ Casper.Network.SDK/Types/EntryPoint.cs | 51 ++++- .../GlobalStateKey/AddressableEntityKey.cs | 72 ++++--- .../Types/GlobalStateKey/GlobalStateKey.cs | 13 +- .../Types/GlobalStateKey/NamedKeyKey.cs | 12 +- Casper.Network.SDK/Types/IEntityIdentifier.cs | 9 + Casper.Network.SDK/Types/InitiatorAddr.cs | 2 + Casper.Network.SDK/Types/PublicKey.cs | 13 +- Casper.Network.SDK/Types/StateIdentifier.cs | 4 +- 14 files changed, 509 insertions(+), 51 deletions(-) create mode 100644 Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs create mode 100644 Casper.Network.SDK/Types/AddressableEntity.cs create mode 100644 Casper.Network.SDK/Types/IEntityIdentifier.cs diff --git a/Casper.Network.SDK.Test/GlobalStateKeyTest.cs b/Casper.Network.SDK.Test/GlobalStateKeyTest.cs index 2e99f18..dc4b405 100644 --- a/Casper.Network.SDK.Test/GlobalStateKeyTest.cs +++ b/Casper.Network.SDK.Test/GlobalStateKeyTest.cs @@ -467,7 +467,7 @@ public void AddressableEntityKeyTest() var entityAddr = "0101010101010101010101010101010101010101010101010101010101010101"; - var entities = new EntityKind[] { EntityKind.System, EntityKind.Account, EntityKind.Contract }; + var entities = new EntityKindEnum[] { EntityKindEnum.System, EntityKindEnum.Account, EntityKindEnum.Contract }; foreach (var entityKind in entities) { var entityKey = $"{ENTITY_PREFIX}{entityKind.ToString().ToLower()}-{entityAddr}"; @@ -513,7 +513,7 @@ public void NamedKeyKeyTest() var entityAddr = "0101010101010101010101010101010101010101010101010101010101010101"; var namedKeyAddr = "0202020202020202020202020202020202020202020202020202020202020202"; - var entities = new EntityKind[] { EntityKind.System, EntityKind.Account, EntityKind.Contract }; + var entities = new EntityKindEnum[] { EntityKindEnum.System, EntityKindEnum.Account, EntityKindEnum.Contract }; foreach (var entityKind in entities) { var namedKeyKey = $"{NAMEDKEY_PREFIX}{ENTITY_PREFIX}{entityKind.ToString().ToLower()}-{entityAddr}-{namedKeyAddr}"; diff --git a/Casper.Network.SDK/JsonRpc/CasperMethods.cs b/Casper.Network.SDK/JsonRpc/CasperMethods.cs index 0c31f38..7fedd10 100644 --- a/Casper.Network.SDK/JsonRpc/CasperMethods.cs +++ b/Casper.Network.SDK/JsonRpc/CasperMethods.cs @@ -130,6 +130,35 @@ public GetAccountInfo(string publicKey, int height) : base("state_get_account_in } } + public class GetEntity : RpcMethod + { + /// + /// Returns an AddressableEntity from the network for a Block from the network + /// + /// A PublicKey, an AccoountHashKey, or an AddressableEntityKey + /// A a block identifier by hash or key. Null for the latest block + public GetEntity(IEntityIdentifier entityIdentifier, IBlockIdentifier blockIdentifier = null) : base("state_get_entity") + { + this.Parameters = new Dictionary + { + { "entity_identifier", entityIdentifier.GetEntityIdentifier() } + }; + + if(blockIdentifier != null) + this.Parameters.Add("block_identifier", blockIdentifier.GetBlockIdentifier()); + } + + /// + /// Returns an AddressableEntity from the network for a Block from the network + /// + /// A string with an addressable entity key. + /// A a block identifier by hash or key. Null for the latest block + public GetEntity(string addressableEntity, IBlockIdentifier blockIdentifier = null) + : this(new AddressableEntityKey(addressableEntity), blockIdentifier) + { + } + } + public class GetItem : RpcMethod { /// @@ -233,7 +262,7 @@ public class QueryBalanceDetails : RpcMethod /// /// The identifier to obtain the purse corresponding to balance query. /// The identifier for the state used for the query, if none is passed, the latest block will be used. - public QueryBalanceDetails(IPurseIdentifier purseIdentifier, IBlockIdentifier blockIdentifier) : base("query_balance_details") + public QueryBalanceDetails(IPurseIdentifier purseIdentifier, IBlockIdentifier blockIdentifier = null) : base("query_balance_details") { this.Parameters = new Dictionary { diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs new file mode 100644 index 0000000..486ca1a --- /dev/null +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Types; + +namespace Casper.Network.SDK.JsonRpc.ResultTypes +{ + /// + /// Result for \"state_get_entity\" RPC response. + /// + [JsonConverter(typeof(GetEntityResultConverter))] + public class GetEntityResult : RpcResult + { + /// + /// An addressable entity. + /// + public AddressableEntity Entity { get; init; } + + /// + /// A legacy account. + /// + public Account LegacyAccount { get; init; } + + /// + /// The merkle proof. + /// + public string MerkleProof { get; init; } + + public class GetEntityResultConverter : JsonConverter + { + public override GetEntityResult Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + string api_version = null; + AddressableEntity entity = null; + Account legacy_account = null; + string merkle_proof = null; + + reader.Read(); + + while (reader.TokenType == JsonTokenType.PropertyName) + { + var property = reader.GetString(); + reader.Read(); + switch (property) + { + case "api_version": + api_version = reader.GetString(); + break; + case "entity": + if (reader.TokenType == JsonTokenType.StartObject) + reader.Read(); + if (reader.TokenType == JsonTokenType.PropertyName) + { + property = reader.GetString(); + reader.Read(); + switch (property) + { + case "AddressableEntity": + if (reader.TokenType != JsonTokenType.Null) + entity = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + break; + case "LegacyAccount": + if (reader.TokenType != JsonTokenType.Null) + legacy_account = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + break; + } + } + + break; + case "merkle_proof": + merkle_proof = reader.GetString(); + break; + } + + reader.Read(); + } + + if(entity == null && legacy_account == null) + throw new JsonException($"Could not deserialize GetEntityResult.."); + + return new GetEntityResult() + { + ApiVersion = api_version, + Entity = entity, + LegacyAccount = legacy_account, + MerkleProof = merkle_proof + }; + } + + public override void Write( + Utf8JsonWriter writer, + GetEntityResult value, + JsonSerializerOptions options) + { + throw new NotImplementedException("Write method for GetEntityResult not yet implemented"); + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index 5a66265..7524aee 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -177,6 +177,50 @@ public async Task> GetAccountInfo(string publi return await SendRpcRequestAsync(method); } + /// + /// Returns an AddressableEntity or a legacy Accountfrom the network for a Block from the network + /// + /// A PublicKey, an AccoountHashKey, or an AddressableEntityKey + /// A block hash for which the information of the entity is queried. Null for most recent information. + public async Task> GetEntity(IEntityIdentifier entityIdentifier, string blockHash = null) + { + var method = new GetEntity(entityIdentifier, blockHash != null ? new BlockIdentifier(blockHash) : null); + return await SendRpcRequestAsync(method); + } + + /// + /// Returns an AddressableEntity or a legacy Accountfrom the network for a Block from the network + /// + /// A PublicKey, an AccoountHashKey, or an AddressableEntityKey + /// A block height for which the information of the entity is queried.. + public async Task> GetEntity(IEntityIdentifier entityIdentifier, ulong blockHeight) + { + var method = new GetEntity(entityIdentifier, new BlockIdentifier(blockHeight)); + return await SendRpcRequestAsync(method); + } + + /// + /// Returns an AddressableEntity or a legacy Accountfrom the network for a Block from the network + /// + /// The entity address to get information of. + /// A block hash for which the information of the entity is queried. Null for most recent information. + public async Task> GetEntity(string entityAddr, string blockHash = null) + { + var method = new GetEntity(entityAddr, blockHash != null ? new BlockIdentifier(blockHash) : null); + return await SendRpcRequestAsync(method); + } + + /// + /// Returns an AddressableEntity or a legacy Accountfrom the network for a Block from the network + /// + /// The entity address to get information of. + /// A block height for which the information of the entity is queried.. + public async Task> GetEntity(string entityAddr, ulong blockHeight) + { + var method = new GetEntity(entityAddr, new BlockIdentifier(blockHeight)); + return await SendRpcRequestAsync(method); + } + /// /// Request a stored value from the network. This RPC is deprecated, use `QueryGlobalState` instead. /// @@ -201,7 +245,7 @@ public async Task> QueryState(string keyHash, ListThe global state key formatted as a string to query the value from the network. /// Height of the block to check the stored value in. /// The path components starting from the key as base (use '/' as separator). - public async Task> QueryGlobalState(string key, int height, + public async Task> QueryGlobalState(string key, ulong height, string path = null) { var method = new QueryGlobalState(key, StateIdentifier.WithBlockHeight(height), path?.Split(new char[] {'/'})); @@ -359,7 +403,7 @@ public async Task> GetAccountBalanceWithBlockHash( /// Purse URef key. /// Height of the block. public async Task> GetAccountBalance(URef purseURef, - int blockHeight) + ulong blockHeight) { var method = new GetBalance(purseURef, StateIdentifier.WithBlockHeight(blockHeight)); return await SendRpcRequestAsync(method); @@ -371,7 +415,7 @@ public async Task> GetAccountBalance(URef purseURe /// The account hash of the account to request the balance. /// Height of the block. public async Task> GetAccountBalance(AccountHashKey accountHash, - int blockHeight) + ulong blockHeight) { var method = new GetBalance(accountHash, StateIdentifier.WithBlockHeight(blockHeight)); return await SendRpcRequestAsync(method); @@ -383,7 +427,7 @@ public async Task> GetAccountBalance(AccountHashKe /// The public key of the account to request the balance. /// Height of the block. public async Task> GetAccountBalance(PublicKey publicKey, - int blockHeight) + ulong blockHeight) { var method = new GetBalance(publicKey, StateIdentifier.WithBlockHeight(blockHeight)); return await SendRpcRequestAsync(method); diff --git a/Casper.Network.SDK/Types/Account.cs b/Casper.Network.SDK/Types/Account.cs index 461499e..8ee8c4c 100644 --- a/Casper.Network.SDK/Types/Account.cs +++ b/Casper.Network.SDK/Types/Account.cs @@ -19,6 +19,12 @@ public class ActionThresholds /// [JsonPropertyName("key_management")] public uint KeyManagement { get; init; } + + /// + /// Threshold for upgrading contracts. + /// + [JsonPropertyName("upgrade_management")] + public uint UpgradeManagement { get; init; } } /// diff --git a/Casper.Network.SDK/Types/AddressableEntity.cs b/Casper.Network.SDK/Types/AddressableEntity.cs new file mode 100644 index 0000000..c80e8a4 --- /dev/null +++ b/Casper.Network.SDK/Types/AddressableEntity.cs @@ -0,0 +1,186 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + public enum SystemEntityType + { + /// + /// Mint contract. + /// + Mint, + + /// + /// Handle Payment contract. + /// + HandlePayment, + + /// + /// Standard Payment contract. + /// + StandardPayment, + + /// + /// Auction contract. + /// + Auction, + } + + + [JsonConverter(typeof(EntityKindConverter))] + public class EntityKind + { + /// + /// Package associated with a native contract implementation. + /// + [JsonConverter(typeof(JsonStringEnumConverter))] + public SystemEntityType? System { get; init; } + + /// + /// Package associated with an Account hash. + /// + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public AccountHashKey Account { get; init; } + + /// + /// Packages associated with Wasm stored on chain. + /// + public bool? SmartContract { get; init; } + + /// + /// Json converter class to serialize/deserialize a Block to/from Json + /// + public class EntityKindConverter : JsonConverter + { + public override EntityKind Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String && + reader.GetString().Equals("SmartContract", StringComparison.InvariantCultureIgnoreCase)) + { + return new EntityKind() + { + SmartContract = true, + }; + } + if (reader.TokenType == JsonTokenType.StartObject) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.PropertyName) + { + var property = reader.GetString(); + reader.Read(); + EntityKind entity = null; + switch (property) + { + case "Account": + entity = new EntityKind() + { + Account = new AccountHashKey(reader.GetString()), + }; + break; + case "System": + entity = new EntityKind() + { + System = EnumCompat.Parse(reader.GetString()), + }; + break; + } + reader.Read(); + if (entity != null) + return entity; + } + } + + throw new JsonException("Cannot deserialize EntityKind"); + } + + public override void Write( + Utf8JsonWriter writer, + EntityKind blockHeader, + JsonSerializerOptions options) + { + throw new NotImplementedException("Write method for EntityKind not yet implemented"); + } + } + } + + /// + /// A message topic. + /// + public class MessageTopic + { + /// + /// The message topic name. + /// + [JsonPropertyName("topic_name")] + public string TopicName { get; init; } + + /// + /// The hash of the name of the message topic. + /// + [JsonPropertyName("topic_name_hash")] + public string TopicNameHash { get; init; } + } + + public class AddressableEntity + { + /// + /// Casper Platform protocol version. + /// + [JsonPropertyName("protocol_version")] + public string ProtocolVersion { get; init; } + + /// + /// The type of Package. + /// + [JsonPropertyName("entity_kind")] + public EntityKind EntityKind { get; init; } + + /// + /// The hex-encoded address of the Package. + /// + [JsonPropertyName("package_hash")] + public string PackageHash { get; init; } + + /// + /// The hash address of the contract wasm. + /// + [JsonPropertyName("byte_code_hash")] + public string ByteCodeHash { get; init; } + + [JsonPropertyName("main_purse")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public URef MainPurse { get; init; } + + /// + /// List of entry points or methods in the package. + /// + [JsonPropertyName("entry_points")] + [JsonConverter(typeof(EntryPoint.NamedEntryPointsConverter))] + public List EntryPoints { get; init; } + + /// + /// Set of public keys allowed to provide signatures on deploys for the package + /// + [JsonPropertyName("associated_keys")] + public List AssociatedKeys { get; init; } + + /// + /// Thresholds that have to be met when executing an action of a certain type. + /// + [JsonPropertyName("action_thresholds")] + public ActionThresholds ActionThresholds { get; init; } + + /// + /// Message topic list for this entity + /// + [JsonPropertyName("message_topics")] + public List MessageTopics { get; init; } + } +} + diff --git a/Casper.Network.SDK/Types/EntryPoint.cs b/Casper.Network.SDK/Types/EntryPoint.cs index 71be3e9..f69a8f0 100644 --- a/Casper.Network.SDK/Types/EntryPoint.cs +++ b/Casper.Network.SDK/Types/EntryPoint.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; @@ -77,13 +78,23 @@ public override void Write( public enum EntryPointType { /// - /// Runs as session code + /// Runs using the calling entity's context. In v1.x this was used for both \"session\" code run using the + /// originating Account's context, and also for \"StoredSession\" code that ran in the caller's context. + /// While this made systemic sense due to the way the runtime context nesting works, this dual usage was + /// very confusing to most human beings. + /// In v2.x the renamed Caller variant is exclusively used for wasm run using the initiating account entity's + /// context. Previously installed 1.x stored session code should continue to work as the binary value matches + /// but we no longer allow such logic to be upgraded, nor do we allow new stored session to be installed. /// - Session, + Caller, /// - /// Runs within contract’s context + /// Runs using the called entity's context. /// - Contract + Called, + /// + /// Extract a subset of bytecode and installs it as a new smart contract. Runs using the called entity's context. + /// + Factory, } /// @@ -142,5 +153,37 @@ public class EntryPoint [JsonPropertyName("ret")] [JsonConverter(typeof(CLTypeInfoConverter))] public CLTypeInfo Ret { get; init; } + + public class NamedEntryPointsConverter : JsonConverter> + { + public override List Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + var namedEntryPoints = JsonSerializer.Deserialize>(ref reader, options); + if (namedEntryPoints != null) + return namedEntryPoints.Select(e => e.EntryPoint).ToList(); + + throw new JsonException("Cannot deserialize Array_of_NamedEntryPoint."); + } + + public override void Write( + Utf8JsonWriter writer, + List value, + JsonSerializerOptions options) + { + throw new NotImplementedException("Write method for Array_of_NamedEntryPoint not yet implemented."); + } + } + } + + public class NamedEntryPoint + { + [JsonPropertyName("name")] + public string Name { get; init; } + + [JsonPropertyName("entry_point")] + public EntryPoint EntryPoint { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs index 063101d..88255dc 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs @@ -1,36 +1,37 @@ using System; +using System.Collections.Generic; using System.IO; using Casper.Network.SDK.Utils; using Org.BouncyCastle.Utilities.Encoders; namespace Casper.Network.SDK.Types { - public enum EntityKind { + public enum EntityKindEnum { /// - /// System variant. + /// Package associated with a native contract implementation. /// System = 0, /// - /// Account variant. + /// Package associated with an Account hash. /// Account = 1, /// - /// SmartContract variant. + /// Package associated with Wasm stored on chain. /// Contract = 2, } - public static class EntityKindExtensions + public static class EntityKinEnumExtensions { - public static string ToKeyPrefix(this EntityKind kind) + public static string ToKeyPrefix(this EntityKindEnum kind) { switch (kind) { - case EntityKind.System: + case EntityKindEnum.System: return "entity-system-"; - case EntityKind.Account: + case EntityKindEnum.Account: return "entity-account-"; - case EntityKind.Contract: + case EntityKindEnum.Contract: return "entity-contract-"; default: return kind.ToString(); @@ -38,18 +39,18 @@ public static string ToKeyPrefix(this EntityKind kind) } } - public class AddressableEntityKey : GlobalStateKey + public class AddressableEntityKey : GlobalStateKey, IEntityIdentifier { - public EntityKind Kind { get; init; } + public EntityKindEnum Kind { get; init; } private static string GetPrefix(string key) { - if (key.StartsWith(EntityKind.System.ToKeyPrefix())) - return EntityKind.System.ToKeyPrefix(); - if (key.StartsWith(EntityKind.Account.ToKeyPrefix())) - return EntityKind.Account.ToKeyPrefix(); - if (key.StartsWith(EntityKind.Contract.ToKeyPrefix())) - return EntityKind.Contract.ToKeyPrefix(); + if (key.StartsWith(EntityKindEnum.System.ToKeyPrefix())) + return EntityKindEnum.System.ToKeyPrefix(); + if (key.StartsWith(EntityKindEnum.Account.ToKeyPrefix())) + return EntityKindEnum.Account.ToKeyPrefix(); + if (key.StartsWith(EntityKindEnum.Contract.ToKeyPrefix())) + return EntityKindEnum.Contract.ToKeyPrefix(); throw new Exception("Unexpected key prefix in NamedKeyKey: " + key); @@ -58,21 +59,21 @@ public AddressableEntityKey(string key) : base(key, GetPrefix(key)) { KeyIdentifier = KeyIdentifier.AddressableEntity; var prefix = GetPrefix(key); - if (EntityKind.System.ToKeyPrefix().Equals(prefix)) - Kind = EntityKind.System; - else if (EntityKind.Account.ToKeyPrefix().Equals(prefix)) - Kind = EntityKind.Account; - else if (EntityKind.Contract.ToKeyPrefix().Equals(prefix)) - Kind = EntityKind.Contract; + if (EntityKindEnum.System.ToKeyPrefix().Equals(prefix)) + Kind = EntityKindEnum.System; + else if (EntityKindEnum.Account.ToKeyPrefix().Equals(prefix)) + Kind = EntityKindEnum.Account; + else if (EntityKindEnum.Contract.ToKeyPrefix().Equals(prefix)) + Kind = EntityKindEnum.Contract; } public AddressableEntityKey(byte[] key) : this( - key[0] == (byte)EntityKind.System - ? EntityKind.System.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) - : (key[0] == (byte)EntityKind.Account - ? EntityKind.Account.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) - : (key[0] == (byte)EntityKind.Account - ? EntityKind.Contract.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + key[0] == (byte)EntityKindEnum.System + ? EntityKindEnum.System.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + : (key[0] == (byte)EntityKindEnum.Account + ? EntityKindEnum.Account.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + : (key[0] == (byte)EntityKindEnum.Account + ? EntityKindEnum.Contract.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) : throw new Exception($"Wrong entity tag '{key[0]}' for AddressableEntityKey.")))) { } @@ -82,8 +83,19 @@ public AddressableEntityKey(BinaryReader reader) : base(null) KeyIdentifier = KeyIdentifier.AddressableEntity; var tag = reader.ReadByte(); var addr = reader.ReadBytes(32); - Kind = (EntityKind)tag; + Kind = (EntityKindEnum)tag; Key = Kind.ToKeyPrefix() + Hex.ToHexString(addr); } + + /// + /// Returns an EntityIdentifier object as defined in the RPC schema for an account hash key. + /// + public Dictionary GetEntityIdentifier() + { + return new Dictionary + { + {"EntityAddr", Key} + }; + } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs index bd8d272..6c4f16e 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs @@ -344,7 +344,7 @@ public override void Write( /// Stores an account in the global state. /// Format: 32-byte length with prefix 'account-hash-'. /// - public class AccountHashKey : GlobalStateKey, IPurseIdentifier + public class AccountHashKey : GlobalStateKey, IPurseIdentifier, IEntityIdentifier { public static string KEYPREFIX = "account-hash-"; @@ -368,6 +368,17 @@ public Dictionary GetPurseIdentifier() {"main_purse_under_account_hash", this.ToString()} }; } + + /// + /// Returns an EntityIdentifier object as defined in the RPC schema for an account hash key. + /// + public Dictionary GetEntityIdentifier() + { + return new Dictionary + { + {"AccountHash", this.ToString()} + }; + } } /// diff --git a/Casper.Network.SDK/Types/GlobalStateKey/NamedKeyKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/NamedKeyKey.cs index b26afa3..bf70dc2 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/NamedKeyKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/NamedKeyKey.cs @@ -11,12 +11,12 @@ public class NamedKeyKey : GlobalStateKey private static string GetPrefix(string key) { - if (key.StartsWith(NAMEDKEYPREFIX+EntityKind.System.ToKeyPrefix())) - return NAMEDKEYPREFIX+EntityKind.System.ToKeyPrefix(); - if (key.StartsWith(NAMEDKEYPREFIX+EntityKind.Account.ToKeyPrefix())) - return NAMEDKEYPREFIX+EntityKind.Account.ToKeyPrefix(); - if (key.StartsWith(NAMEDKEYPREFIX+EntityKind.Contract.ToKeyPrefix())) - return NAMEDKEYPREFIX+EntityKind.Contract.ToKeyPrefix(); + if (key.StartsWith(NAMEDKEYPREFIX+EntityKindEnum.System.ToKeyPrefix())) + return NAMEDKEYPREFIX+EntityKindEnum.System.ToKeyPrefix(); + if (key.StartsWith(NAMEDKEYPREFIX+EntityKindEnum.Account.ToKeyPrefix())) + return NAMEDKEYPREFIX+EntityKindEnum.Account.ToKeyPrefix(); + if (key.StartsWith(NAMEDKEYPREFIX+EntityKindEnum.Contract.ToKeyPrefix())) + return NAMEDKEYPREFIX+EntityKindEnum.Contract.ToKeyPrefix(); throw new Exception("Unexpected key prefix in NamedKeyKey: " + key); } diff --git a/Casper.Network.SDK/Types/IEntityIdentifier.cs b/Casper.Network.SDK/Types/IEntityIdentifier.cs new file mode 100644 index 0000000..289ae9c --- /dev/null +++ b/Casper.Network.SDK/Types/IEntityIdentifier.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Casper.Network.SDK.Types +{ + public interface IEntityIdentifier + { + public Dictionary GetEntityIdentifier(); + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/InitiatorAddr.cs b/Casper.Network.SDK/Types/InitiatorAddr.cs index 6a1e87d..a3f7b70 100644 --- a/Casper.Network.SDK/Types/InitiatorAddr.cs +++ b/Casper.Network.SDK/Types/InitiatorAddr.cs @@ -11,6 +11,7 @@ public class InitiatorAddr /// The public key of the initiator /// [JsonPropertyName("PublicKey")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonConverter(typeof(PublicKey.PublicKeyConverter))] public PublicKey PublicKey { get; init; } @@ -18,6 +19,7 @@ public class InitiatorAddr /// The account hash derived from the public key of the initiator /// [JsonPropertyName("AccountHash")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public AccountHashKey AccountHash { get; init; } diff --git a/Casper.Network.SDK/Types/PublicKey.cs b/Casper.Network.SDK/Types/PublicKey.cs index 5d883b5..6aa8868 100644 --- a/Casper.Network.SDK/Types/PublicKey.cs +++ b/Casper.Network.SDK/Types/PublicKey.cs @@ -18,7 +18,7 @@ namespace Casper.Network.SDK.Types /// /// A wrapper for a Public Key. Provides signature verification functionality. /// - public class PublicKey: IPurseIdentifier + public class PublicKey: IPurseIdentifier, IEntityIdentifier { /// /// Byte array without the Key algorithm identifier. @@ -276,6 +276,17 @@ public Dictionary GetPurseIdentifier() {"main_purse_under_public_key", this.ToString()} }; } + + /// + /// Returns an EntityIdentifier object as defined in the RPC schema for a public key. + /// + public Dictionary GetEntityIdentifier() + { + return new Dictionary + { + {"PublicKey", this.ToString()} + }; + } #region Cast operators diff --git a/Casper.Network.SDK/Types/StateIdentifier.cs b/Casper.Network.SDK/Types/StateIdentifier.cs index c951a80..14f3eaa 100644 --- a/Casper.Network.SDK/Types/StateIdentifier.cs +++ b/Casper.Network.SDK/Types/StateIdentifier.cs @@ -6,7 +6,7 @@ public class StateIdentifier { private string _stateRootHash; private string _blockHash; - private int? _blockHeight; + private ulong? _blockHeight; private StateIdentifier() { @@ -32,7 +32,7 @@ public static StateIdentifier WithBlockHash(string blockHash) }; } - public static StateIdentifier WithBlockHeight(int blockHeight) + public static StateIdentifier WithBlockHeight(ulong blockHeight) { return new StateIdentifier { From 9aab087c0c2b1a315a76b9edd2866e2d811ee061 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 17 May 2024 11:58:26 +0200 Subject: [PATCH 010/126] CSDK-118 refactor Transform to TransformV1. Implement TransformV2 and its inner types. Signed-off-by: David Hernando --- .../NctlMyDictContractTest.cs | 2 +- Casper.Network.SDK.Test/TransferDeployTest.cs | 2 +- .../DeployApprovalByteSerializer.cs | 4 +- .../JsonRpc/ResultTypes/GetDeployResult.cs | 9 +- .../ResultTypes/QueryGlobalStateResult.cs | 2 +- Casper.Network.SDK/SSE/DeployAccepted.cs | 2 +- .../Types/{DeployApproval.cs => Approval.cs} | 5 +- Casper.Network.SDK/Types/Bid.cs | 2 +- Casper.Network.SDK/Types/BidKind.cs | 20 ++ Casper.Network.SDK/Types/ByteCode.cs | 17 ++ Casper.Network.SDK/Types/ContractPackage.cs | 2 +- Casper.Network.SDK/Types/Deploy.cs | 8 +- Casper.Network.SDK/Types/ExecutionEffect.cs | 4 +- Casper.Network.SDK/Types/ExecutionResult.cs | 79 ++++++- Casper.Network.SDK/Types/Message.cs | 28 +++ Casper.Network.SDK/Types/NamedKey.cs | 18 ++ Casper.Network.SDK/Types/Package.cs | 107 ++++++++++ Casper.Network.SDK/Types/Reservation.cs | 19 ++ Casper.Network.SDK/Types/StoredValue.cs | 49 ++++- .../Types/{Transform.cs => TransformV1.cs} | 67 +++--- Casper.Network.SDK/Types/TransformV2.cs | 198 ++++++++++++++++++ Casper.Network.SDK/Types/UnbondingPurse.cs | 11 +- Casper.Network.SDK/Types/ValidatorWeight.cs | 2 +- 23 files changed, 593 insertions(+), 64 deletions(-) rename Casper.Network.SDK/Types/{DeployApproval.cs => Approval.cs} (85%) create mode 100644 Casper.Network.SDK/Types/BidKind.cs create mode 100644 Casper.Network.SDK/Types/ByteCode.cs create mode 100644 Casper.Network.SDK/Types/Message.cs create mode 100644 Casper.Network.SDK/Types/Package.cs create mode 100644 Casper.Network.SDK/Types/Reservation.cs rename Casper.Network.SDK/Types/{Transform.cs => TransformV1.cs} (75%) create mode 100644 Casper.Network.SDK/Types/TransformV2.cs diff --git a/Casper.Network.SDK.Test/NctlMyDictContractTest.cs b/Casper.Network.SDK.Test/NctlMyDictContractTest.cs index 5e3a2e2..b11f659 100644 --- a/Casper.Network.SDK.Test/NctlMyDictContractTest.cs +++ b/Casper.Network.SDK.Test/NctlMyDictContractTest.cs @@ -49,7 +49,7 @@ public async Task WaitContractDeploymentTest() var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); var getResponse = await _client.GetDeploy(_contractDeployHash, tokenSource.Token); - var execResult = getResponse.Parse().ExecutionResults.First(); + var execResult = getResponse.Parse().ExecutionResult; Assert.IsTrue(execResult.IsSuccess); Assert.AreEqual(64, execResult.BlockHash.Length); Assert.IsNull(execResult.ErrorMessage); diff --git a/Casper.Network.SDK.Test/TransferDeployTest.cs b/Casper.Network.SDK.Test/TransferDeployTest.cs index 1ef8f00..51b6e75 100644 --- a/Casper.Network.SDK.Test/TransferDeployTest.cs +++ b/Casper.Network.SDK.Test/TransferDeployTest.cs @@ -41,7 +41,7 @@ public void Transfer1() var deploy = new Deploy(header, payment, session); - deploy.AddApproval(new DeployApproval() + deploy.AddApproval(new Approval() { Signature = Signature.FromHexString("012dbf03817a51794a8e19e0724884075e6d1fbec326b766ecfa6658b41f81290da85e23b24e88b1c8d9761185c961daee1adab0649912a6477bcd2e69bd91bd08"), Signer = PublicKey.FromHexString("01027c04a0210afdf4a83328d57e8c2a12247a86d872fb53367f22a84b1b53d2a9") diff --git a/Casper.Network.SDK/ByteSerializers/DeployApprovalByteSerializer.cs b/Casper.Network.SDK/ByteSerializers/DeployApprovalByteSerializer.cs index b9f62d4..d3e6762 100644 --- a/Casper.Network.SDK/ByteSerializers/DeployApprovalByteSerializer.cs +++ b/Casper.Network.SDK/ByteSerializers/DeployApprovalByteSerializer.cs @@ -3,9 +3,9 @@ namespace Casper.Network.SDK.ByteSerializers { - public class DeployApprovalByteSerializer : BaseByteSerializer, IByteSerializer + public class DeployApprovalByteSerializer : BaseByteSerializer, IByteSerializer { - public byte[] ToBytes(DeployApproval source) + public byte[] ToBytes(Approval source) { var ms = new MemoryStream(); WriteBytes(ms, source.Signer.GetBytes()); diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs index bf2fa2a..02edec6 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs @@ -19,18 +19,17 @@ public class GetDeployResult : RpcResult /// /// The map of block hash to execution result. /// - [JsonPropertyName("execution_results")] - [JsonConverter(typeof(GenericListConverter))] - public List ExecutionResults { get; init; } + [JsonPropertyName("execution_result")] + public ExecutionResult ExecutionResult { get; init; } /// - /// The hash of this deploy's block. + /// The hash of the block in which the deploy was executed. /// [JsonPropertyName("block_hash")] public string BlockHash { get; init; } /// - /// The height of this deploy's block. + /// The height of the block in which the deploy was executed. /// [JsonPropertyName("block_height")] public ulong BlockHeight { get; init; } diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs index b5c5710..6faaf6e 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs @@ -13,7 +13,7 @@ public class QueryGlobalStateResult : RpcResult /// [JsonPropertyName("block_header")] [JsonConverter(typeof(BlockHeader.BlockHeaderConverter))] - public BlockHeaderV1 BlockHeader { get; init; } + public BlockHeader BlockHeader { get; init; } /// /// The stored value. diff --git a/Casper.Network.SDK/SSE/DeployAccepted.cs b/Casper.Network.SDK/SSE/DeployAccepted.cs index 009ab9b..d3458f6 100644 --- a/Casper.Network.SDK/SSE/DeployAccepted.cs +++ b/Casper.Network.SDK/SSE/DeployAccepted.cs @@ -12,7 +12,7 @@ public class DeployAccepted : Deploy // This is an alias of Deploy to be used in SSE. // public DeployAccepted(string hash, DeployHeader header, ExecutableDeployItem payment, - ExecutableDeployItem session, List approvals) + ExecutableDeployItem session, List approvals) : base(hash, header, payment, session, approvals) { } diff --git a/Casper.Network.SDK/Types/DeployApproval.cs b/Casper.Network.SDK/Types/Approval.cs similarity index 85% rename from Casper.Network.SDK/Types/DeployApproval.cs rename to Casper.Network.SDK/Types/Approval.cs index ba1699b..d2727e8 100644 --- a/Casper.Network.SDK/Types/DeployApproval.cs +++ b/Casper.Network.SDK/Types/Approval.cs @@ -1,15 +1,14 @@ using System.Text.Json.Serialization; -using Casper.Network.SDK.Converters; namespace Casper.Network.SDK.Types { /// /// Signature and Public Key of the signer. /// - public class DeployApproval + public class Approval { /// - /// Signature of a deploy. + /// Signature of a deploy or transaction. /// [JsonPropertyName("signature")] [JsonConverter(typeof(Signature.SignatureConverter))] diff --git a/Casper.Network.SDK/Types/Bid.cs b/Casper.Network.SDK/Types/Bid.cs index 09b244a..2eae346 100644 --- a/Casper.Network.SDK/Types/Bid.cs +++ b/Casper.Network.SDK/Types/Bid.cs @@ -45,7 +45,7 @@ public class Bid public BigInteger StakedAmount { get; init; } /// - /// Validator public key + /// Validator public key. /// [JsonPropertyName("validator_public_key")] [JsonConverter(typeof(PublicKey.PublicKeyConverter))] diff --git a/Casper.Network.SDK/Types/BidKind.cs b/Casper.Network.SDK/Types/BidKind.cs new file mode 100644 index 0000000..23888e2 --- /dev/null +++ b/Casper.Network.SDK/Types/BidKind.cs @@ -0,0 +1,20 @@ +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + /// + /// Auction bid variants. + /// + public class BidKind + { + [JsonPropertyName("Unified")] + public Bid Unified { get; init; } + + [JsonPropertyName("Validator")] + public Bid Validator { get; init; } + + [JsonPropertyName("Delegator")] + [JsonConverter(typeof(Delegator.DelegatorConverter))] + public Delegator Delegator { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/ByteCode.cs b/Casper.Network.SDK/Types/ByteCode.cs new file mode 100644 index 0000000..d7439cf --- /dev/null +++ b/Casper.Network.SDK/Types/ByteCode.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + /// + /// A container for contract's Wasm bytes. + /// + public class ByteCode + { + [JsonPropertyName("kind")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public ByteCodeKind Kind { get; init; } + + [JsonPropertyName("bytes")] + public string Bytes { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/ContractPackage.cs b/Casper.Network.SDK/Types/ContractPackage.cs index 1318dcb..0881aba 100644 --- a/Casper.Network.SDK/Types/ContractPackage.cs +++ b/Casper.Network.SDK/Types/ContractPackage.cs @@ -98,7 +98,7 @@ public class ContractPackage public List Versions { get; init; } /// - /// The current state of node reactor. + /// The current state of the contract package. /// [JsonPropertyName("lock_status")] [JsonConverter(typeof(JsonStringEnumConverter))] diff --git a/Casper.Network.SDK/Types/Deploy.cs b/Casper.Network.SDK/Types/Deploy.cs index 14b2692..af6850c 100644 --- a/Casper.Network.SDK/Types/Deploy.cs +++ b/Casper.Network.SDK/Types/Deploy.cs @@ -18,7 +18,7 @@ public class Deploy /// List of signers and signatures for this Deploy. /// [JsonPropertyName("approvals")] - public List Approvals { get; } = new List(); + public List Approvals { get; } = new List(); /// /// A hash over the header of the deploy. @@ -94,7 +94,7 @@ public string SerializeToJson() [JsonConstructor] public Deploy(string hash, DeployHeader header, ExecutableDeployItem payment, - ExecutableDeployItem session, List approvals) + ExecutableDeployItem session, List approvals) { this.Hash = hash; this.Header = header; @@ -130,7 +130,7 @@ public void Sign(KeyPair keyPair) { byte[] signature = keyPair.Sign(Hex.Decode(this.Hash)); - Approvals.Add(new DeployApproval() + Approvals.Add(new Approval() { Signature = Signature.FromRawBytes(signature, keyPair.PublicKey.KeyAlgorithm), Signer = keyPair.PublicKey @@ -140,7 +140,7 @@ public void Sign(KeyPair keyPair) /// /// Adds an approval to the deploy. No check is done to the approval signature. /// - public void AddApproval(DeployApproval approval) + public void AddApproval(Approval approval) { this.Approvals.Add(approval); } diff --git a/Casper.Network.SDK/Types/ExecutionEffect.cs b/Casper.Network.SDK/Types/ExecutionEffect.cs index 1bec2ff..9ebd592 100644 --- a/Casper.Network.SDK/Types/ExecutionEffect.cs +++ b/Casper.Network.SDK/Types/ExecutionEffect.cs @@ -19,7 +19,7 @@ public class ExecutionEffect /// The journal of execution transforms. /// [JsonPropertyName("transforms")] - [JsonConverter(typeof(GenericListConverter))] - public List Transforms { get; init; } + [JsonConverter(typeof(GenericListConverter))] + public List Transforms { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/ExecutionResult.cs b/Casper.Network.SDK/Types/ExecutionResult.cs index b11d41d..db844c2 100644 --- a/Casper.Network.SDK/Types/ExecutionResult.cs +++ b/Casper.Network.SDK/Types/ExecutionResult.cs @@ -10,7 +10,7 @@ namespace Casper.Network.SDK.Types /// /// The result of executing a single deploy. /// - public class ExecutionResult + public class ExecutionResultV1 { [JsonIgnore] public bool IsSuccess { get; init; } @@ -45,14 +45,14 @@ public class ExecutionResult [JsonConverter(typeof(GenericListConverter))] public List Transfers { get; init; } - public ExecutionResult() + public ExecutionResultV1() { Transfers = new List(); } - public class ExecutionResultConverter : JsonConverter + public class ExecutionResultConverter : JsonConverter { - public override ExecutionResult Read( + public override ExecutionResultV1 Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) @@ -63,7 +63,7 @@ public override ExecutionResult Read( string blockHash = null; string result = null; bool readResult = false; - ExecutionResult executionResult = null; + ExecutionResultV1 executionResult = null; while (reader.TokenType == JsonTokenType.PropertyName) { @@ -87,7 +87,7 @@ public override ExecutionResult Read( if (reader.TokenType != JsonTokenType.StartObject) throw new JsonException($"Object expected after '{result}' during ExecutionResult deserialization"); - executionResult = JsonSerializer.Deserialize(ref reader); + executionResult = JsonSerializer.Deserialize(ref reader); reader.Read(); // end Success/Failure object @@ -103,7 +103,7 @@ public override ExecutionResult Read( if (executionResult == null) throw new JsonException("Could not deserialize ExecutionResult object"); - return new ExecutionResult + return new ExecutionResultV1 { BlockHash = blockHash, IsSuccess = result.Equals("Success", StringComparison.InvariantCultureIgnoreCase), @@ -116,11 +116,74 @@ public override ExecutionResult Read( public override void Write( Utf8JsonWriter writer, - ExecutionResult item, + ExecutionResultV1 item, JsonSerializerOptions options) { throw new NotImplementedException("Serialization of ExecutionResult not implemented"); } } } + + public class ExecutionResultV2 + { + /// + /// What was the maximum allowed gas limit for this transaction?. + /// + [JsonPropertyName("limit")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Limit { get; init; } + + /// + /// How much gas was consumed executing this transaction. + /// + [JsonPropertyName("consumed")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Consumed { get; init; } + + /// + /// How much was paid for this transaction. + /// + [JsonPropertyName("cost")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Cost { get; init; } + + /// + /// If there is no error message, this execution was processed successfully. If there is an error message, this + /// execution failed to fully process for the stated reason. + /// + [JsonPropertyName("error_message")] + public string ErrorMessage { get; init; } + + /// + /// A record of transfers performed while executing this transaction. + /// + [JsonPropertyName("transfers")] + public List Transfers { get; init; } + + /// + /// The size estimate of the transaction + /// + [JsonPropertyName("size_estimate")] + public UInt64 SizeEstimate { get; init; } + + /// + /// A log of all transforms produced during execution. + /// + [JsonPropertyName("effects")] + [JsonConverter(typeof(GenericListConverter))] + public List Effect { get; init; } + } + + public class ExecutionResult + { + /// + /// Version 1 of execution result type. + /// + public ExecutionResultV1 Version1 { get; init; } + + /// + /// Version 2 of execution result type. + /// + public ExecutionResultV2 Version2 { get; init; } + } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Message.cs b/Casper.Network.SDK/Types/Message.cs new file mode 100644 index 0000000..3d58810 --- /dev/null +++ b/Casper.Network.SDK/Types/Message.cs @@ -0,0 +1,28 @@ +using System; +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + /// + /// Summary of a message topic that will be stored in global state. + /// + public class MessageTopicSummary + { + /// + /// Block timestamp in which these messages were emitted. + /// + [JsonPropertyName("blocktime")] + public UInt64 BlockTime { get; init; } + + /// + /// Number of messages in this topic. + /// + [JsonPropertyName("message_count")] + public UInt32 MessageCount { get; init; } + } + + public class MessageChecksum + { + + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/NamedKey.cs b/Casper.Network.SDK/Types/NamedKey.cs index f7a0db2..8d4b052 100644 --- a/Casper.Network.SDK/Types/NamedKey.cs +++ b/Casper.Network.SDK/Types/NamedKey.cs @@ -20,4 +20,22 @@ public class NamedKey [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public GlobalStateKey Key { get; init; } } + + /// + /// A named key in an addressable entity. + /// + public class NamedKeyValue + { + /// + /// The name of the entry. + /// + [JsonPropertyName("name")] + public CLValue Name { get; init; } + + /// + /// The value of the entry: a casper `Key` type. + /// + [JsonPropertyName("named_key")] + public CLValue Key { get; init; } + } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Package.cs b/Casper.Network.SDK/Types/Package.cs new file mode 100644 index 0000000..c142ffb --- /dev/null +++ b/Casper.Network.SDK/Types/Package.cs @@ -0,0 +1,107 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; + +namespace Casper.Network.SDK.Types +{ + /// + /// Major element of `ProtocolVersion` combined with `EntityVersion`. + /// + public class EntityVersion + { + /// + /// Automatically incremented value for a contract version within a major `ProtocolVersion`. + /// + [JsonPropertyName("entity_version")] + public uint Version { get; init; } + + /// + /// Major element of `ProtocolVersion` a `ContractVersion` is compatible with. + /// + [JsonPropertyName("protocol_version_major")] + public uint ProtocolVersionMajor { get; init; } + } + + public enum PackageStatus + { + /// + /// The package is locked and cannot be versioned. + /// + Locked, + /// + /// The package is unlocked and can be versioned. + /// + Unlocked, + } + + public class EntityVersionAndHash + { + /// + /// Major element of `ProtocolVersion` combined with `EntityVersion`. + /// + [JsonPropertyName("entity_version_key")] + public EntityVersion EntityVersion { get; init; } + + /// + /// The hex-encoded address of the addressable entity. + /// + [JsonPropertyName("addressable_entity_hash")] + public string AddressableEntityHash { get; init; } + } + + public class NamedUserGroup + { + /// + /// The hex-encoded address of the addressable entity. + /// + [JsonPropertyName("group_name")] + public string Name { get; init; } + + /// + /// List of URefs associated with the group. + /// + [JsonPropertyName("group_users")] + [JsonConverter(typeof(GenericListConverter))] + public List Users { get; init; } + } + + /// + /// Entity definition, metadata, and security container. + /// + public class Package + { + /// + /// Key used to add or disable versions. + /// + [JsonPropertyName("access_key")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public URef AccessKey { get; init; } + + /// + /// All versions (enabled & disabled). + /// + [JsonPropertyName("versions")] + public List Versions { get; init; } + + /// + /// Collection of disabled entity versions. The runtime will not permit disabled entity versions to be executed. + /// + [JsonPropertyName("disabled_versions")] + public List DisabledVersions { get; init; } + + /// + /// Mapping maintaining the set of URefs associated with each "user group". This can be used + /// to control access to methods in a particular version of the entity. A method is callable + /// by any context which "knows" any of the URefs associated with the method's user group. + /// + [JsonPropertyName("groups")] + public List Groups { get; init; } + + /// + /// The current state of the contract package. + /// + [JsonPropertyName("lock_status")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public LockStatus LockStatus { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Reservation.cs b/Casper.Network.SDK/Types/Reservation.cs new file mode 100644 index 0000000..37333e4 --- /dev/null +++ b/Casper.Network.SDK/Types/Reservation.cs @@ -0,0 +1,19 @@ +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + /// + /// Container for bytes recording location, type and data for a gas reservation. + /// + public class Reservation + { + [JsonPropertyName("receipt")] + public string Receipt { get; init; } + + [JsonPropertyName("reservation_kind")] + public byte ReservationKind { get; init; } + + [JsonPropertyName("reservation_data")] + public string ReservationData { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/StoredValue.cs b/Casper.Network.SDK/Types/StoredValue.cs index d52c9c5..104099a 100644 --- a/Casper.Network.SDK/Types/StoredValue.cs +++ b/Casper.Network.SDK/Types/StoredValue.cs @@ -9,6 +9,7 @@ namespace Casper.Network.SDK.Types /// /// A wrapper class for different types of values stored in the global state. /// + [JsonConverter(typeof(StoredValue.StoredValueConverter))] public class StoredValue { public Contract Contract { get; init; } @@ -21,7 +22,7 @@ public class StoredValue public ContractPackage ContractPackage { get; init; } - public Transfer Transfer { get; init; } + public TransferV1 LegacyTransfer { get; init; } public DeployInfo DeployInfo { get; init; } @@ -29,8 +30,50 @@ public class StoredValue public Bid Bid { get; init; } - public List Withdraw { get; init; } - + public List Withdraw { get; init; } + + public List Unbonding { get; init; } + + /// + /// Stores an addressable entity. + /// + public AddressableEntity AddressableEntity { get; init; } + + /// + /// Stores a BidKind. + /// + // public BidKind BidKind { get; init; } + + /// + /// Stores a package. + /// + public Package Package { get; init; } + + /// + /// A record of byte code. + /// + public ByteCode ByteCode { get; init; } + + /// + /// Stores a message topic. + /// + public MessageTopicSummary MessageTopic { get; init; } + + /// + /// Stores a message digest. + /// + public string Message { get; init; } + + /// + /// Stores a NamedKey. + /// + public NamedKeyValue NamedKey { get; init; } + + /// + /// Stores location, type and data for a gas reservation. + /// + // public Reservation Reservation { get; init; } + public class StoredValueConverter : JsonConverter { public override StoredValue Read diff --git a/Casper.Network.SDK/Types/Transform.cs b/Casper.Network.SDK/Types/TransformV1.cs similarity index 75% rename from Casper.Network.SDK/Types/Transform.cs rename to Casper.Network.SDK/Types/TransformV1.cs index e9ee8e9..83eb94f 100644 --- a/Casper.Network.SDK/Types/Transform.cs +++ b/Casper.Network.SDK/Types/TransformV1.cs @@ -9,7 +9,7 @@ namespace Casper.Network.SDK.Types /// /// Enumeration of transformation types used in the execution of a deploy. /// - public enum TransformType + public enum TransformKindV1 { Identity, WriteContractWasm, @@ -30,12 +30,15 @@ public enum TransformType AddKeys, Failure, WriteUnbonding, + WriteAddressableEntity, + Prune, + WriteBidKind, } /// /// A transformation performed while executing a deploy. /// - public class Transform + public class TransformV1 { /// /// The formatted string of the `Key`. @@ -46,16 +49,16 @@ public class Transform /// /// The type of transform /// - public TransformType Type { get; init; } + public TransformKindV1 Type { get; init; } /// /// Data associated to some type of transforms /// public object Value { get; init; } - public class TransformConverter : JsonConverter + public class TransformV1Converter : JsonConverter { - public override Transform Read( + public override TransformV1 Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) @@ -66,7 +69,7 @@ public override Transform Read( reader.Read(); // start object string key = null; - TransformType? type = null; + TransformKindV1? type = null; object value = null; while (reader.TokenType == JsonTokenType.PropertyName) @@ -84,7 +87,7 @@ public override Transform Read( { var stype = reader.GetString(); if (stype != null) - type = EnumCompat.Parse(stype); + type = EnumCompat.Parse(stype); reader.Read(); } else if (reader.TokenType == JsonTokenType.StartObject) @@ -92,70 +95,78 @@ public override Transform Read( reader.Read(); var stype = reader.GetString(); if (stype != null) - type = EnumCompat.Parse(stype); + type = EnumCompat.Parse(stype); reader.Read(); switch (type) { - case TransformType.WriteCLValue: + case TransformKindV1.WriteCLValue: value = JsonSerializer.Deserialize(ref reader, options); reader.Read(); // end object break; - case TransformType.WriteAccount: + case TransformKindV1.WriteAccount: value = GlobalStateKey.FromString(reader.GetString()); reader.Read(); // end object break; - case TransformType.WriteDeployInfo: + case TransformKindV1.WriteDeployInfo: value = JsonSerializer.Deserialize(ref reader, options); reader.Read(); // end object break; - case TransformType.WriteEraInfo: + case TransformKindV1.WriteEraInfo: value = JsonSerializer.Deserialize(ref reader, options); reader.Read(); // end object break; - case TransformType.WriteTransfer: - value = JsonSerializer.Deserialize(ref reader, options); + case TransformKindV1.WriteTransfer: + value = JsonSerializer.Deserialize(ref reader, options); reader.Read(); // end object break; - case TransformType.WriteBid: + case TransformKindV1.WriteBid: value = JsonSerializer.Deserialize(ref reader, options); reader.Read(); // end object break; - case TransformType.WriteWithdraw: - value = JsonSerializer.Deserialize>(ref reader, options); + case TransformKindV1.WriteWithdraw: + value = JsonSerializer.Deserialize>(ref reader, options); reader.Read(); // end object break; - case TransformType.AddInt32: + case TransformKindV1.AddInt32: value = reader.GetInt32(); reader.Read(); break; - case TransformType.AddUInt64: + case TransformKindV1.AddUInt64: value = reader.GetUInt64(); reader.Read(); break; - case TransformType.AddUInt128: + case TransformKindV1.AddUInt128: value = BigInteger.Parse(reader.GetString() ?? "0"); reader.Read(); break; - case TransformType.AddUInt256: + case TransformKindV1.AddUInt256: value = BigInteger.Parse(reader.GetString() ?? "0"); reader.Read(); break; - case TransformType.AddUInt512: + case TransformKindV1.AddUInt512: value = BigInteger.Parse(reader.GetString() ?? "0"); reader.Read(); break; - case TransformType.AddKeys: + case TransformKindV1.AddKeys: value = JsonSerializer.Deserialize>(ref reader, options); reader.Read(); // end array break; - case TransformType.Failure: + case TransformKindV1.Failure: value = reader.GetString(); reader.Read(); break; - case TransformType.WriteUnbonding: + case TransformKindV1.WriteUnbonding: value = JsonSerializer.Deserialize>(ref reader, options); reader.Read(); break; + case TransformKindV1.Prune: + value = GlobalStateKey.FromString(reader.GetString()); + reader.Read(); + break; + case TransformKindV1.WriteBidKind: + value = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + break; } reader.Read(); //end object @@ -165,10 +176,10 @@ public override Transform Read( if (key != null & type != null) { - return new Transform() + return new TransformV1() { Key = GlobalStateKey.FromString(key), - Type = type ?? TransformType.Identity, + Type = type ?? TransformKindV1.Identity, Value = value }; } @@ -178,7 +189,7 @@ public override Transform Read( public override void Write( Utf8JsonWriter writer, - Transform value, + TransformV1 value, JsonSerializerOptions options) { throw new NotImplementedException("Write method for Transform not yet implemented"); diff --git a/Casper.Network.SDK/Types/TransformV2.cs b/Casper.Network.SDK/Types/TransformV2.cs new file mode 100644 index 0000000..abd4478 --- /dev/null +++ b/Casper.Network.SDK/Types/TransformV2.cs @@ -0,0 +1,198 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + public enum TransformKindV2 + { + /// + /// An identity transformation that does not modify a value in the global state. Created as a result of + /// reading from the global state. + /// + Identity, + /// + /// Writes a new value (StoredValue) in the global state. + /// + Write, + /// + /// A wrapping addition of an `i32` to an existing numeric value (not necessarily an `i32`) in the global state. + /// + AddInt32, + /// + /// A wrapping addition of a `u64` to an existing numeric value (not necessarily an `u64`) in the global state. + /// + AddUInt64, + /// + /// A wrapping addition of a `U128` to an existing numeric value (not necessarily an `U128`) in the global state. + /// + AddUInt128, + /// + /// A wrapping addition of a `U256` to an existing numeric value (not necessarily an `U256`) in the global state. + /// + AddUInt256, + /// + /// A wrapping addition of a `U512` to an existing numeric value (not necessarily an `U512`) in the global state. + /// + AddUInt512, + /// + /// Adds new named keys to an existing entry in the global state.\n\nThis transform assumes that the existing stored + /// value is either an Account or a Contract. + /// + AddKeys, + /// + /// Removes the pathing to the global state entry of the specified key. The pruned element remains reachable from + /// previously generated global state root hashes, but will not be included in the next generated global state + /// root hash and subsequent state accumulated from it. + /// + Prune, + /// + /// Represents the case where applying a transform would cause an error. + /// + Failure, + } + + /// + /// Representation of a single transformation occurring during execution.\n\nNote that all arithmetic + /// variants of `TransformKindV2` are commutative which means that a given collection of them can be + /// executed in any order to produce the same end result. + /// + public class Kind + { + + } + + /// + /// A transformation performed while executing a deploy. + /// + public class TransformV2 + { + /// + /// The formatted string of the `Key`. + /// + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public GlobalStateKey Key { get; init; } + + /// + /// Representation of a single transformation occurring during execution.\n\nNote that all arithmetic + /// variants of `TransformKindV2` are commutative which means that a given collection of them can be + /// executed in any order to produce the same end result. + /// + public TransformKindV2 TransformKind { get; init; } + + /// + /// Data associated to some type of transforms + /// + public object Value { get; init; } + + public class TransformV2Converter : JsonConverter + { + public override TransformV2 Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException("Cannot deserialize Transform. StartObject expected"); + + reader.Read(); // start object + + string key = null; + TransformKindV2? kind = null; + object value = null; + + while (reader.TokenType == JsonTokenType.PropertyName) + { + var field = reader.GetString(); + reader.Read(); + if (field == "key") + { + key = reader.GetString(); + reader.Read(); + } + else if (field == "kind") + { + if (reader.TokenType == JsonTokenType.String) + { + var stype = reader.GetString(); + if (stype != null) + kind = EnumCompat.Parse(stype); + reader.Read(); + } + else if (reader.TokenType == JsonTokenType.StartObject) + { + reader.Read(); + var stype = reader.GetString(); + if (stype != null) + kind = EnumCompat.Parse(stype); + reader.Read(); + switch (kind) + { + case TransformKindV2.Write: + value = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); // end object + break; + case TransformKindV2.AddInt32: + value = reader.GetInt32(); + reader.Read(); + break; + case TransformKindV2.AddUInt64: + value = reader.GetUInt64(); + reader.Read(); + break; + case TransformKindV2.AddUInt128: + value = BigInteger.Parse(reader.GetString() ?? "0"); + reader.Read(); + break; + case TransformKindV2.AddUInt256: + value = BigInteger.Parse(reader.GetString() ?? "0"); + reader.Read(); + break; + case TransformKindV2.AddUInt512: + value = BigInteger.Parse(reader.GetString() ?? "0"); + reader.Read(); + break; + case TransformKindV2.AddKeys: + value = JsonSerializer.Deserialize>(ref reader, options); + reader.Read(); // end array + break; + case TransformKindV2.Prune: + value = GlobalStateKey.FromString(reader.GetString()); + reader.Read(); + break; + case TransformKindV2.Failure: + value = reader.GetString(); + reader.Read(); + break; + } + + reader.Read(); //end object + } + } + } + + if (key != null & kind != null) + { + return new TransformV2() + { + Key = GlobalStateKey.FromString(key), + TransformKind = kind ?? TransformKindV2.Identity, + Value = value + }; + } + + throw new JsonException("Incomplete transform"); + } + + public override void Write( + Utf8JsonWriter writer, + TransformV2 value, + JsonSerializerOptions options) + { + throw new NotImplementedException("Write method for TransformV2 not yet implemented"); + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/UnbondingPurse.cs b/Casper.Network.SDK/Types/UnbondingPurse.cs index dfe3557..03ced7d 100644 --- a/Casper.Network.SDK/Types/UnbondingPurse.cs +++ b/Casper.Network.SDK/Types/UnbondingPurse.cs @@ -5,9 +5,9 @@ namespace Casper.Network.SDK.Types { /// - /// Information of an unbonding or delegation withdrawal + /// Information of a delegation withdrawal (legacy structure) /// - public class UnbondingPurse + public class WithdrawPurse { /// /// Unbonding Amount. @@ -39,6 +39,13 @@ public class UnbondingPurse [JsonPropertyName("validator_public_key")] [JsonConverter(typeof(PublicKey.PublicKeyConverter))] public PublicKey ValidatorPublicKey { get; init; } + } + + /// + /// Information of an unbonding or delegation withdrawal + /// + public class UnbondingPurse : WithdrawPurse + { /// /// The validator public key to re-delegate to. diff --git a/Casper.Network.SDK/Types/ValidatorWeight.cs b/Casper.Network.SDK/Types/ValidatorWeight.cs index 1c9474d..7ba8191 100644 --- a/Casper.Network.SDK/Types/ValidatorWeight.cs +++ b/Casper.Network.SDK/Types/ValidatorWeight.cs @@ -6,7 +6,7 @@ namespace Casper.Network.SDK.Types { /// - /// A validator's weight. + /// A validator's public key paired with its weight, i.e. the total number of motes staked by it and its delegators. /// public class ValidatorWeight { From 9e1ffd0d291d02728e4c470a597095aa66be0bb0 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 21 May 2024 16:50:04 +0200 Subject: [PATCH 011/126] CSDK-119 update for rc2 Signed-off-by: David Hernando --- .../JsonRpc/ResultTypes/GetEntityResult.cs | 68 ++++++++++++------- 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs index 486ca1a..813f656 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs @@ -17,6 +17,16 @@ public class GetEntityResult : RpcResult /// public AddressableEntity Entity { get; init; } + /// + /// Array of named keys present in the entity. + /// + public List NamedKeys { get; init; } + + /// + /// Array of entry points defined in the entity + /// + public List EntryPoints { get; init; } + /// /// A legacy account. /// @@ -36,9 +46,11 @@ public override GetEntityResult Read( { string api_version = null; AddressableEntity entity = null; - Account legacy_account = null; + var namedKeys = new List(); + var entryPoints = new List(); + Account legacyAccount = null; string merkle_proof = null; - + uint skippedEntityWrapperCount = 0; reader.Read(); while (reader.TokenType == JsonTokenType.PropertyName) @@ -51,27 +63,24 @@ public override GetEntityResult Read( api_version = reader.GetString(); break; case "entity": - if (reader.TokenType == JsonTokenType.StartObject) - reader.Read(); - if (reader.TokenType == JsonTokenType.PropertyName) - { - property = reader.GetString(); - reader.Read(); - switch (property) - { - case "AddressableEntity": - if (reader.TokenType != JsonTokenType.Null) - entity = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - break; - case "LegacyAccount": - if (reader.TokenType != JsonTokenType.Null) - legacy_account = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - break; - } - } - + // parse only if we already read AddressableEntity property. Otherwise, this is a wrapper for an AnddressableEntity or a LegacyAccount + if (++skippedEntityWrapperCount >= 2) + entity = JsonSerializer.Deserialize(ref reader, options); + break; + case "AddressableEntity": + // continue, this is a wrapper for entity (the actual AddressableEntity), named_keys and entry_points. + ++skippedEntityWrapperCount; + break; + case "named_keys": + namedKeys = JsonSerializer.Deserialize>(ref reader, options); + break; + case "entry_points": + entryPoints = JsonSerializer.Deserialize>(ref reader, options); + break; + case "LegacyAccount": + if (reader.TokenType != JsonTokenType.Null) + legacyAccount = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); break; case "merkle_proof": merkle_proof = reader.GetString(); @@ -79,16 +88,23 @@ public override GetEntityResult Read( } reader.Read(); + + while (reader.TokenType == JsonTokenType.EndObject && --skippedEntityWrapperCount > 0) + reader.Read(); // skip entity wrapper EndObject token and continue parsing GetEntityResult properties } - if(entity == null && legacy_account == null) - throw new JsonException($"Could not deserialize GetEntityResult.."); + reader.Read(); // skip outer type EndObject token + + if(entity == null && legacyAccount == null) + throw new JsonException($"Could not deserialize GetEntityResult."); return new GetEntityResult() { ApiVersion = api_version, Entity = entity, - LegacyAccount = legacy_account, + NamedKeys = namedKeys, + EntryPoints = entryPoints, + LegacyAccount = legacyAccount, MerkleProof = merkle_proof }; } From 9b55b6cf543a4ed0a81ce9d7debd9740a7178795 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 21 May 2024 17:01:33 +0200 Subject: [PATCH 012/126] CSDK-118 added EntryPointKey and EntryPoint types. added Bid and BidKind Update StoredValue Signed-off-by: David Hernando --- .../JsonRpc/ResultTypes/GetDeployResult.cs | 18 +-- .../JsonRpc/ResultTypes/GetEntityResult.cs | 68 ++++++----- .../ResultTypes/SpeculativeExecutionResult.cs | 4 +- Casper.Network.SDK/NetCasperClient.cs | 2 +- Casper.Network.SDK/SSE/DeployProcessed.cs | 6 +- Casper.Network.SDK/Types/AddressableEntity.cs | 23 ++-- Casper.Network.SDK/Types/Bid.cs | 10 +- Casper.Network.SDK/Types/BidKind.cs | 1 - Casper.Network.SDK/Types/Delegator.cs | 109 ++++-------------- Casper.Network.SDK/Types/EntryPoint.cs | 61 ++++++++-- Casper.Network.SDK/Types/ExecutionInfo.cs | 25 ++++ .../Types/GlobalStateKey/EntryPointKey.cs | 47 ++++++++ .../Types/GlobalStateKey/GlobalStateKey.cs | 10 +- Casper.Network.SDK/Types/StoredValue.cs | 4 +- 14 files changed, 223 insertions(+), 165 deletions(-) create mode 100644 Casper.Network.SDK/Types/ExecutionInfo.cs create mode 100644 Casper.Network.SDK/Types/GlobalStateKey/EntryPointKey.cs diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs index 02edec6..7ac65f1 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs @@ -17,21 +17,9 @@ public class GetDeployResult : RpcResult public Deploy Deploy { get; init; } /// - /// The map of block hash to execution result. + /// Execution info, if available. /// - [JsonPropertyName("execution_result")] - public ExecutionResult ExecutionResult { get; init; } - - /// - /// The hash of the block in which the deploy was executed. - /// - [JsonPropertyName("block_hash")] - public string BlockHash { get; init; } - - /// - /// The height of the block in which the deploy was executed. - /// - [JsonPropertyName("block_height")] - public ulong BlockHeight { get; init; } + [JsonPropertyName("execution_info")] + public ExecutionInfo ExecutionInfo { get; init; } } } diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs index 486ca1a..813f656 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs @@ -17,6 +17,16 @@ public class GetEntityResult : RpcResult /// public AddressableEntity Entity { get; init; } + /// + /// Array of named keys present in the entity. + /// + public List NamedKeys { get; init; } + + /// + /// Array of entry points defined in the entity + /// + public List EntryPoints { get; init; } + /// /// A legacy account. /// @@ -36,9 +46,11 @@ public override GetEntityResult Read( { string api_version = null; AddressableEntity entity = null; - Account legacy_account = null; + var namedKeys = new List(); + var entryPoints = new List(); + Account legacyAccount = null; string merkle_proof = null; - + uint skippedEntityWrapperCount = 0; reader.Read(); while (reader.TokenType == JsonTokenType.PropertyName) @@ -51,27 +63,24 @@ public override GetEntityResult Read( api_version = reader.GetString(); break; case "entity": - if (reader.TokenType == JsonTokenType.StartObject) - reader.Read(); - if (reader.TokenType == JsonTokenType.PropertyName) - { - property = reader.GetString(); - reader.Read(); - switch (property) - { - case "AddressableEntity": - if (reader.TokenType != JsonTokenType.Null) - entity = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - break; - case "LegacyAccount": - if (reader.TokenType != JsonTokenType.Null) - legacy_account = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - break; - } - } - + // parse only if we already read AddressableEntity property. Otherwise, this is a wrapper for an AnddressableEntity or a LegacyAccount + if (++skippedEntityWrapperCount >= 2) + entity = JsonSerializer.Deserialize(ref reader, options); + break; + case "AddressableEntity": + // continue, this is a wrapper for entity (the actual AddressableEntity), named_keys and entry_points. + ++skippedEntityWrapperCount; + break; + case "named_keys": + namedKeys = JsonSerializer.Deserialize>(ref reader, options); + break; + case "entry_points": + entryPoints = JsonSerializer.Deserialize>(ref reader, options); + break; + case "LegacyAccount": + if (reader.TokenType != JsonTokenType.Null) + legacyAccount = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); break; case "merkle_proof": merkle_proof = reader.GetString(); @@ -79,16 +88,23 @@ public override GetEntityResult Read( } reader.Read(); + + while (reader.TokenType == JsonTokenType.EndObject && --skippedEntityWrapperCount > 0) + reader.Read(); // skip entity wrapper EndObject token and continue parsing GetEntityResult properties } - if(entity == null && legacy_account == null) - throw new JsonException($"Could not deserialize GetEntityResult.."); + reader.Read(); // skip outer type EndObject token + + if(entity == null && legacyAccount == null) + throw new JsonException($"Could not deserialize GetEntityResult."); return new GetEntityResult() { ApiVersion = api_version, Entity = entity, - LegacyAccount = legacy_account, + NamedKeys = namedKeys, + EntryPoints = entryPoints, + LegacyAccount = legacyAccount, MerkleProof = merkle_proof }; } diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/SpeculativeExecutionResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/SpeculativeExecutionResult.cs index b0144ca..21564a4 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/SpeculativeExecutionResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/SpeculativeExecutionResult.cs @@ -18,7 +18,7 @@ public class SpeculativeExecutionResult : RpcResult /// The result of executing the Deploy. /// [JsonPropertyName("execution_result")] - [JsonConverter(typeof(ExecutionResult.ExecutionResultConverter))] - public ExecutionResult ExecutionResult { get; init; } + [JsonConverter(typeof(ExecutionResultV1.ExecutionResultConverter))] + public ExecutionResultV1 ExecutionResult { get; init; } } } diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index 7524aee..b6fa8c2 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -521,7 +521,7 @@ public async Task> GetDeploy(string deployHash, { var response = await SendRpcRequestAsync(method); if (!cancellationToken.CanBeCanceled || - response.Result.GetProperty("execution_results").GetArrayLength() > 0) + response.Result.GetProperty("execution_info").GetArrayLength() > 0) return response; await Task.Delay(10000); } diff --git a/Casper.Network.SDK/SSE/DeployProcessed.cs b/Casper.Network.SDK/SSE/DeployProcessed.cs index 5e7185f..4cfb7d0 100644 --- a/Casper.Network.SDK/SSE/DeployProcessed.cs +++ b/Casper.Network.SDK/SSE/DeployProcessed.cs @@ -6,7 +6,7 @@ namespace Casper.Network.SDK.SSE { /// - /// A Deploy that has been executed, committed and forms part of a Block.. + /// A Deploy that has been executed, committed and forms part of a Block.. /// public class DeployProcessed { @@ -52,7 +52,7 @@ public class DeployProcessed /// The result of executing a this Deploy. /// [JsonPropertyName("execution_result")] - [JsonConverter(typeof(ExecutionResult.ExecutionResultConverter))] - public ExecutionResult ExecutionResult { get; init; } + [JsonConverter(typeof(ExecutionResultV1.ExecutionResultConverter))] + public ExecutionResultV1 ExecutionResult { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/AddressableEntity.cs b/Casper.Network.SDK/Types/AddressableEntity.cs index c80e8a4..d80835d 100644 --- a/Casper.Network.SDK/Types/AddressableEntity.cs +++ b/Casper.Network.SDK/Types/AddressableEntity.cs @@ -47,7 +47,7 @@ public class EntityKind /// /// Packages associated with Wasm stored on chain. /// - public bool? SmartContract { get; init; } + public string SmartContract { get; init; } /// /// Json converter class to serialize/deserialize a Block to/from Json @@ -59,14 +59,6 @@ public override EntityKind Read( Type typeToConvert, JsonSerializerOptions options) { - if (reader.TokenType == JsonTokenType.String && - reader.GetString().Equals("SmartContract", StringComparison.InvariantCultureIgnoreCase)) - { - return new EntityKind() - { - SmartContract = true, - }; - } if (reader.TokenType == JsonTokenType.StartObject) { reader.Read(); @@ -89,6 +81,12 @@ public override EntityKind Read( System = EnumCompat.Parse(reader.GetString()), }; break; + case "SmartContract": + entity = new EntityKind() + { + SmartContract = reader.GetString(), + }; + break; } reader.Read(); if (entity != null) @@ -157,13 +155,6 @@ public class AddressableEntity [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public URef MainPurse { get; init; } - /// - /// List of entry points or methods in the package. - /// - [JsonPropertyName("entry_points")] - [JsonConverter(typeof(EntryPoint.NamedEntryPointsConverter))] - public List EntryPoints { get; init; } - /// /// Set of public keys allowed to provide signatures on deploys for the package /// diff --git a/Casper.Network.SDK/Types/Bid.cs b/Casper.Network.SDK/Types/Bid.cs index 2eae346..af5a44e 100644 --- a/Casper.Network.SDK/Types/Bid.cs +++ b/Casper.Network.SDK/Types/Bid.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Numerics; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; @@ -28,8 +29,13 @@ public class Bid /// The delegators. /// [JsonPropertyName("delegators")] - [JsonConverter(typeof(GenericListConverter))] - public List Delegators { get; init; } + public List PublicKeyAndDelegators { get; init; } + + public List Delegators + { + get { return PublicKeyAndDelegators.Select(pd => pd.Delegator).ToList(); } + init { } + } /// /// `true` if validator has been "evicted" diff --git a/Casper.Network.SDK/Types/BidKind.cs b/Casper.Network.SDK/Types/BidKind.cs index 23888e2..5b3a33c 100644 --- a/Casper.Network.SDK/Types/BidKind.cs +++ b/Casper.Network.SDK/Types/BidKind.cs @@ -14,7 +14,6 @@ public class BidKind public Bid Validator { get; init; } [JsonPropertyName("Delegator")] - [JsonConverter(typeof(Delegator.DelegatorConverter))] public Delegator Delegator { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Delegator.cs b/Casper.Network.SDK/Types/Delegator.cs index 007c9ed..ec17538 100644 --- a/Casper.Network.SDK/Types/Delegator.cs +++ b/Casper.Network.SDK/Types/Delegator.cs @@ -6,6 +6,16 @@ namespace Casper.Network.SDK.Types { + public class PublicKeyAndDelegator + { + [JsonPropertyName("delegator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey DelegatorPublicKey { get; init; } + + [JsonPropertyName("delegator")] + public Delegator Delegator { get; init; } + } + /// /// A delegator associated with the given validator. /// @@ -14,107 +24,32 @@ public class Delegator /// /// The purse that was used for delegating. /// + [JsonPropertyName("bonding_purse")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public URef BondingPurse { get; init; } /// /// Public key of the validator /// - public PublicKey Delegatee { get; init; } + [JsonPropertyName("validator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey ValidatorPublicKey { get; init; } /// /// Public Key of the delegator /// - public PublicKey PublicKey { get; init; } + [JsonPropertyName("delegator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey DelegatorPublicKey { get; init; } /// /// Amount of Casper token (in motes) delegated /// + [JsonPropertyName("staked_amount")] + [JsonConverter(typeof(BigIntegerConverter))] public BigInteger StakedAmount { get; init; } - public VestingSchedule VestingSchedule { get; init; } - - public class DelegatorConverter : JsonConverter, IDeserializeAsList - { - public bool DeserializeAsList { get { return true; } } - - public override Delegator Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - // two possibilities called 'Delegator' and 'JsonDelegator' in the rpc schema - // An array of delegators is returned in the GetAuctionState response - // A dictionary of delegators is returned in the QueryGlobalState response for a Bid key - // This methods parses both but returns a common Delegator object in both cases - - string delegatorPublicKey = null; - if (reader.TokenType == JsonTokenType.PropertyName) - { - delegatorPublicKey = reader.GetString(); - reader.Read(); - } - - if (reader.TokenType != JsonTokenType.StartObject) - throw new JsonException("Could not deserialize Delegator. Start object token expected."); - - reader.Read(); //start object - - string public_key = null; - string amount = null; - string bonding_purse = null; - string validator_public_key = null; - VestingSchedule vesting_schedule = null; - - while (reader.TokenType == JsonTokenType.PropertyName) - { - var property = reader.GetString()?.ToLowerInvariant(); - reader.Read(); - - switch (property) - { - case "delegator_public_key": - case "public_key": - public_key = reader.GetString(); - reader.Read(); - break; - case "bonding_purse": - bonding_purse = reader.GetString(); - reader.Read(); - break; - case "staked_amount": - amount = reader.GetString(); - reader.Read(); - break; - case "validator_public_key": - case "delegatee": - validator_public_key = reader.GetString(); - reader.Read(); - break; - case "vesting_schedule": - if(reader.TokenType != JsonTokenType.Null) - vesting_schedule = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - break; - } - } - - return new Delegator() - { - PublicKey = PublicKey.FromHexString(public_key), - StakedAmount = BigInteger.Parse(amount), - BondingPurse = new URef(bonding_purse), - Delegatee = PublicKey.FromHexString(validator_public_key), - VestingSchedule = vesting_schedule - }; - } - - public override void Write( - Utf8JsonWriter writer, - Delegator value, - JsonSerializerOptions options) - { - throw new NotImplementedException("Write method for Delegator not yet implemented"); - } - } + // [JsonPropertyName("vesting_schedule")] + // public VestingSchedule VestingSchedule { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/EntryPoint.cs b/Casper.Network.SDK/Types/EntryPoint.cs index f69a8f0..15490cf 100644 --- a/Casper.Network.SDK/Types/EntryPoint.cs +++ b/Casper.Network.SDK/Types/EntryPoint.cs @@ -97,6 +97,22 @@ public enum EntryPointType Factory, } + public enum EntryPointPayment + { + /// + /// The caller must cover cost. + /// + Caller, + /// + /// Will cover cost to execute self but not cost of any subsequent invoked contracts. + /// + SelfOnly, + /// + /// Will cover cost to execute self and the cost of any subsequent invoked contracts. + /// + SelfOnward, + } + /// /// Parameter to a method /// @@ -119,7 +135,7 @@ public class Parameter /// /// Type signature of a method. Order of arguments matter since can be referenced by index as well as name. /// - public class EntryPoint + public class EntryPointV1 { /// /// Access control options for a contract entry point @@ -141,6 +157,13 @@ public class EntryPoint [JsonConverter(typeof(JsonStringEnumConverter))] public EntryPointType EntryPointType { get; init; } + /// + /// Specifies who pays for the invocation and execution of the entrypoint. + /// + [JsonPropertyName("entry_point_payment")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public EntryPointPayment EntryPointPayment { get; init; } + /// /// Name of the entry point /// @@ -154,6 +177,33 @@ public class EntryPoint [JsonConverter(typeof(CLTypeInfoConverter))] public CLTypeInfo Ret { get; init; } + + } + + public class EntryPointV2 + { + public EntryPointV2() + { + throw new NotImplementedException("V2CasperVm entry point not yet implemented"); + } + } + + public class EntryPoint + { + public EntryPointV1 V1CasperVm { get; init; } + + public EntryPointV2 V2CasperVm { get; init; } + } + + //TODO: Remove this if finally is not used + public class NamedEntryPoint + { + [JsonPropertyName("name")] + public string Name { get; init; } + + [JsonPropertyName("entry_point")] + public EntryPoint EntryPoint { get; init; } + public class NamedEntryPointsConverter : JsonConverter> { public override List Read( @@ -177,13 +227,4 @@ public override void Write( } } } - - public class NamedEntryPoint - { - [JsonPropertyName("name")] - public string Name { get; init; } - - [JsonPropertyName("entry_point")] - public EntryPoint EntryPoint { get; init; } - } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/ExecutionInfo.cs b/Casper.Network.SDK/Types/ExecutionInfo.cs new file mode 100644 index 0000000..29c04df --- /dev/null +++ b/Casper.Network.SDK/Types/ExecutionInfo.cs @@ -0,0 +1,25 @@ +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + public class ExecutionInfo + { + /// + /// The hash of the block in which the deploy was executed. + /// + [JsonPropertyName("block_hash")] + public string BlockHash { get; init; } + + /// + /// The height of the block in which the deploy was executed. + /// + [JsonPropertyName("block_height")] + public ulong BlockHeight { get; init; } + + /// + /// The map of block hash to execution result. + /// + [JsonPropertyName("execution_result")] + public ExecutionResult ExecutionResult { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey/EntryPointKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/EntryPointKey.cs new file mode 100644 index 0000000..9865f31 --- /dev/null +++ b/Casper.Network.SDK/Types/GlobalStateKey/EntryPointKey.cs @@ -0,0 +1,47 @@ +using System; +using Casper.Network.SDK.Utils; + +namespace Casper.Network.SDK.Types +{ + public class EntryPointKey : GlobalStateKey + { + public static string ENTRYPOINT_PREFIX = "entry-point-"; + public static string V1_PREFIX = "v1-"; + public static string V2_PREFIX = "v2-"; + + public AddressableEntityKey AddressableEntity { get; init; } + + public string NameHash { get; init; } + + public UInt32 Separator { get; init; } + + public EntryPointKey(string key) : base(key) + { + KeyIdentifier = KeyIdentifier.EntryPoint; + + if (!key.StartsWith(ENTRYPOINT_PREFIX)) + throw new ArgumentException($"Key not valid. It should start with '{ENTRYPOINT_PREFIX}'."); + key = key.Substring(ENTRYPOINT_PREFIX.Length); + + if (key.StartsWith(V1_PREFIX)) + { + key = key.Substring(V1_PREFIX.Length); + var parts = key.Split('-'); + if (parts.Length != 4) + throw new Exception("Key not valid. It should have an entity address and a name hash."); + + AddressableEntity = new AddressableEntityKey($"{parts[0]}-{parts[1]}-{parts[2]}"); + NameHash = parts[3]; + } + else if (key.StartsWith(V2_PREFIX)) + { + throw new Exception($"entry-point-v2 not yet supported. {key}."); + } + } + + public EntryPointKey(byte[] key) : base(null) + { + throw new Exception($"entry-point key from bytes not yet supported. {key}."); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs index 6c4f16e..0409232 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs @@ -104,6 +104,10 @@ public enum KeyIdentifier /// A `Key` under which a hold on a purse balance is stored. /// BalanceHold = 0x16, + /// + /// A `Key` under which a entrypoint record is written. + /// + EntryPoint = 0x17, } /// @@ -207,6 +211,8 @@ public static GlobalStateKey FromString(string value) return new ByteCodeKey(value); if (value.StartsWith("message-")) return new MessageKey(value); + if (value.StartsWith("entry-point-")) + return new EntryPointKey(value); throw new ArgumentException($"Key not valid. Unknown key prefix in \"{value}\"."); } @@ -241,6 +247,7 @@ public static GlobalStateKey FromBytes(byte[] bytes) 0x14 => new NamedKeyKey(bytes.Slice(1)), 0x15 => new BlockGlobalAddrKey(bytes.Slice(1)), 0x16 => new BalanceHoldKey(bytes.Slice(1)), + 0x17 => new EntryPointKey(bytes.Slice(1)), _ => throw new ArgumentException($"Unknown key identifier '{bytes[0]}'") }; } @@ -293,7 +300,8 @@ public override bool CanConvert(Type typeToConvert) typeToConvert == typeof(MessageKey) || typeToConvert == typeof(NamedKeyKey) || typeToConvert == typeof(BlockGlobalAddrKey) || - typeToConvert == typeof(BalanceHoldKey); + typeToConvert == typeof(BalanceHoldKey) || + typeToConvert == typeof(EntryPointKey); } public override JsonConverter CreateConverter( diff --git a/Casper.Network.SDK/Types/StoredValue.cs b/Casper.Network.SDK/Types/StoredValue.cs index 104099a..47e33bd 100644 --- a/Casper.Network.SDK/Types/StoredValue.cs +++ b/Casper.Network.SDK/Types/StoredValue.cs @@ -72,7 +72,9 @@ public class StoredValue /// /// Stores location, type and data for a gas reservation. /// - // public Reservation Reservation { get; init; } + public Reservation Reservation { get; init; } + + public EntryPoint EntryPoint { get; init; } public class StoredValueConverter : JsonConverter { From 050ebf400076573cffa51100c1c389bd15dabe19 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 22 May 2024 15:35:45 +0200 Subject: [PATCH 013/126] CSDK-179 finalitysignature parsing Signed-off-by: David Hernando --- Casper.Network.SDK/SSE/FinalitySignature.cs | 132 +++++++++++++++++++- 1 file changed, 130 insertions(+), 2 deletions(-) diff --git a/Casper.Network.SDK/SSE/FinalitySignature.cs b/Casper.Network.SDK/SSE/FinalitySignature.cs index 4748f2c..b7ac4e2 100644 --- a/Casper.Network.SDK/SSE/FinalitySignature.cs +++ b/Casper.Network.SDK/SSE/FinalitySignature.cs @@ -1,12 +1,14 @@ +using System; +using System.Text.Json; using System.Text.Json.Serialization; using Casper.Network.SDK.Types; namespace Casper.Network.SDK.SSE { /// - /// Server-Side event sent after each block finalization + /// A validator's signature of a block, confirming it is finalized. Produced in Casper v1.x /// - public class FinalitySignature + public class FinalitySignatureV1 : FinalitySignature { /// /// The block hash @@ -34,4 +36,130 @@ public class FinalitySignature [JsonConverter(typeof(Signature.SignatureConverter))] public Signature Signature { get; init; } } + + /// + /// A validator's signature of a block, confirming it is finalized. Produced in Casper v2.x + /// + public class FinalitySignatureV2 : FinalitySignatureV1 + { + /// + /// The block height + /// + [JsonPropertyName("block_height")] + public ulong BlockHeight { get; init; } + + /// + /// The hash of the chain name of the associated block. + /// + [JsonPropertyName("chain_name_hash")] + public string ChainNameHash { get; init; } + } + + [JsonConverter(typeof(FinalitySignature.FinalitySignatureConverter))] + public interface IFinalitySignature + { + public int Version { get; } + + public FinalitySignatureV1 FinalitySignatureV1 { get; } + + public FinalitySignatureV2 FinalitySignatureV2 { get; } + } + + /// + /// A validator's signature of a block, confirming it is finalized. + /// + public class FinalitySignature : IFinalitySignature + { + protected int _version; + + /// + /// Returns the version of the transfer. + /// + public int Version + { + get { return _version; } + } + + /// + /// Returns the transfer as a Version1 transfer object. + /// + FinalitySignatureV1 IFinalitySignature.FinalitySignatureV1 => this as FinalitySignatureV1; + + /// + /// Returns the transfer as a Version2 transfer object. + /// + FinalitySignatureV2 IFinalitySignature.FinalitySignatureV2 => this as FinalitySignatureV2; + + /// + /// Json converter class to serialize/deserialize a Block to/from Json + /// + public class FinalitySignatureConverter : JsonConverter + { + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert == typeof(IFinalitySignature) || + typeToConvert == typeof(FinalitySignature); + } + + public override IFinalitySignature Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + reader.Read(); + var version = reader.GetString(); + reader.Read(); + switch (version) + { + case "V1": + { + var finalitySignature1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + finalitySignature1._version = 1; + return finalitySignature1; + } + case "V2": + var finalitySignature2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + finalitySignature2._version = 2; + return finalitySignature2; + default: + throw new JsonException("Expected V1 or V2"); + } + + ; + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + IFinalitySignature finalitySignature, + JsonSerializerOptions options) + { + switch (finalitySignature.Version) + { + case 1: + writer.WritePropertyName("V1"); + writer.WriteStartObject(); + JsonSerializer.Serialize(finalitySignature as FinalitySignatureV1, options); + writer.WriteEndObject(); + break; + case 2: + writer.WritePropertyName("V2"); + writer.WriteStartObject(); + JsonSerializer.Serialize(finalitySignature as FinalitySignatureV2, options); + writer.WriteEndObject(); + break; + default: + throw new JsonException($"Unexpected finality signature version {finalitySignature.Version}"); + } + } + } + } } \ No newline at end of file From 21c773ec093436c6100591be22947db5baac937c Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 22 May 2024 15:45:49 +0200 Subject: [PATCH 014/126] CSDK-179 finalitysignature parsing Signed-off-by: David Hernando --- Casper.Network.SDK.Test/SSETypesTest.cs | 37 +++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 Casper.Network.SDK.Test/SSETypesTest.cs diff --git a/Casper.Network.SDK.Test/SSETypesTest.cs b/Casper.Network.SDK.Test/SSETypesTest.cs new file mode 100644 index 0000000..7e7b630 --- /dev/null +++ b/Casper.Network.SDK.Test/SSETypesTest.cs @@ -0,0 +1,37 @@ +using System.IO; +using System.Text.Json; +using Casper.Network.SDK.SSE; +using NUnit.Framework; + +namespace NetCasperTest +{ + public class SSETypesTest + { + [Test] + public void FinalitySignatureV2Test() + { + string testFile = TestContext.CurrentContext.TestDirectory + "/TestData/finality_signature_v2.json"; + var json = File.ReadAllText(testFile); + + var doc = System.Text.Json.JsonDocument.Parse(json); + + json = doc.RootElement.GetProperty("FinalitySignature").GetRawText(); + // Assert.AreEqual(3, doc.RootElement.EnumerateObject().Count()); + // Assert.AreEqual("U512", ); + var value = JsonSerializer.Deserialize(json); + Assert.IsNotNull(value); + + var v2 = value.FinalitySignatureV2; + Assert.IsNotNull(v2); + Assert.AreEqual("13a5603c1dad7d4e4d2ce81313c35043172e1535363c3ec428f559dff5704ea5", v2.BlockHash); + Assert.AreEqual(54, v2.BlockHeight); + Assert.AreEqual(5, v2.EraId); + Assert.AreEqual("8a09603fc862b15412b60a050d71f69c57601b6da7382dd56b9a3f300822bb75", v2.ChainNameHash); + Assert.AreEqual("01a12e3601b4d5c82177231910adaabf50d8a52416e756efee1694ee7534ae16fdb59a7370d564715615736074850b9255ee246c8ffa2502c167c6ef8a86b06504", + v2.Signature.ToHexString().ToLowerInvariant()); + Assert.AreEqual("01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + v2.PublicKey.ToString().ToLowerInvariant()); + + } + } +} \ No newline at end of file From 3505f0872e4f35adb4f0cc34066893c51156cfa8 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 22 May 2024 15:46:57 +0200 Subject: [PATCH 015/126] CSDK-179 finalitysignature parsing Signed-off-by: David Hernando --- .../TestData/block_added_v2.json | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Casper.Network.SDK.Test/TestData/block_added_v2.json diff --git a/Casper.Network.SDK.Test/TestData/block_added_v2.json b/Casper.Network.SDK.Test/TestData/block_added_v2.json new file mode 100644 index 0000000..9a33854 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/block_added_v2.json @@ -0,0 +1,44 @@ +{ + "BlockAdded": { + "block_hash": "13a5603c1dad7d4e4d2ce81313c35043172e1535363c3ec428f559dff5704ea5", + "block": { + "Version2": { + "hash": "13a5603c1dad7d4e4d2ce81313c35043172e1535363c3ec428f559dff5704ea5", + "header": { + "parent_hash": "8d14ccfde187f71c0d0b6a2e51298e851521a52e6df21ff8c572412477498106", + "state_root_hash": "a8a33e0b5a36950eda1ef918cb959a6bb0a56aeca356b6d670003d041bb348e9", + "body_hash": "337a4c9e510e01e142a19e5d81203bdc43e59a4f9039288c01f7b89370e1d104", + "random_bit": false, + "accumulated_seed": "b86abf8486be8b34621ac2d519886e894e1bef647b556e42a2d4c8b3104270a1", + "era_end": null, + "timestamp": "2024-05-22T07:22:36.684Z", + "era_id": 5, + "height": 54, + "protocol_version": "2.0.0", + "proposer": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "current_gas_price": 1, + "last_switch_block_hash": "1d0103963e72d14e760106267d28f4f20ef2eb08dc8a0101e5f18e6f18859af0" + }, + "body": { + "transactions": { + "0": [], + "1": [], + "2": [], + "3": [] + }, + "rewarded_signatures": [ + [ + 248 + ], + [ + 0 + ], + [ + 0 + ] + ] + } + } + } + } +} From 5e17fb4e26c73fed6128bdf06b20569c84d77d76 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 22 May 2024 15:47:21 +0200 Subject: [PATCH 016/126] CSDK-179 finalitysignature parsing Signed-off-by: David Hernando --- .../TestData/finality_signature_v2.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 Casper.Network.SDK.Test/TestData/finality_signature_v2.json diff --git a/Casper.Network.SDK.Test/TestData/finality_signature_v2.json b/Casper.Network.SDK.Test/TestData/finality_signature_v2.json new file mode 100644 index 0000000..6d94fee --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/finality_signature_v2.json @@ -0,0 +1,12 @@ +{ + "FinalitySignature": { + "V2": { + "block_hash": "13a5603c1dad7d4e4d2ce81313c35043172e1535363c3ec428f559dff5704ea5", + "block_height": 54, + "era_id": 5, + "chain_name_hash": "8a09603fc862b15412b60a050d71f69c57601b6da7382dd56b9a3f300822bb75", + "signature": "01a12e3601b4d5c82177231910adaabf50d8a52416e756efee1694ee7534ae16fdb59a7370d564715615736074850b9255ee246c8ffa2502c167c6ef8a86b06504", + "public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b" + } + } +} \ No newline at end of file From 8701f07d13212fb770f1af7563cc625a005d3a84 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 30 May 2024 16:45:46 +0200 Subject: [PATCH 017/126] CSDK-120 CSDK-121 info_get_transaction and account_put_transaction implementation Signed-off-by: David Hernando --- .../ByteSerializers/BaseByteSerializer.cs | 12 + .../TransactionV1ByteSerializer.cs | 189 +++++++++ Casper.Network.SDK/JsonRpc/CasperMethods.cs | 56 +++ .../ResultTypes/GetTransactionResult.cs | 35 ++ .../ResultTypes/PutTransactionResult.cs | 17 + Casper.Network.SDK/NetCasperClient.cs | 64 +++ Casper.Network.SDK/Types/Deploy.cs | 1 - Casper.Network.SDK/Types/PricingMode.cs | 159 +++++++ Casper.Network.SDK/Types/Transaction.cs | 18 + .../Types/TransactionEntryPoint.cs | 110 +++++ Casper.Network.SDK/Types/TransactionHash.cs | 2 +- .../Types/TransactionScheduling.cs | 106 +++++ Casper.Network.SDK/Types/TransactionTarget.cs | 395 ++++++++++++++++++ Casper.Network.SDK/Types/TransactionV1.cs | 233 +++++++++++ Casper.Network.SDK/Types/TransactionV1Body.cs | 40 ++ .../Types/TransactionV1Header.cs | 54 +++ 16 files changed, 1489 insertions(+), 2 deletions(-) create mode 100644 Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs create mode 100644 Casper.Network.SDK/JsonRpc/ResultTypes/GetTransactionResult.cs create mode 100644 Casper.Network.SDK/JsonRpc/ResultTypes/PutTransactionResult.cs create mode 100644 Casper.Network.SDK/Types/PricingMode.cs create mode 100644 Casper.Network.SDK/Types/Transaction.cs create mode 100644 Casper.Network.SDK/Types/TransactionEntryPoint.cs create mode 100644 Casper.Network.SDK/Types/TransactionScheduling.cs create mode 100644 Casper.Network.SDK/Types/TransactionTarget.cs create mode 100644 Casper.Network.SDK/Types/TransactionV1.cs create mode 100644 Casper.Network.SDK/Types/TransactionV1Body.cs create mode 100644 Casper.Network.SDK/Types/TransactionV1Header.cs diff --git a/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs b/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs index 26698e1..3dff02b 100644 --- a/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs +++ b/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs @@ -44,5 +44,17 @@ protected static void WriteString(MemoryStream ms, string value) ms.Write(lenBytes); ms.Write(valueBytes); } + + protected static void WriteMaybeUInteger(MemoryStream ms, uint? maybeValue) + { + WriteByte(ms, (byte)(maybeValue.HasValue ? 0x01 : 0x00)); + + if (maybeValue.HasValue) + { + var bytes = BitConverter.GetBytes(maybeValue.Value); + if(!BitConverter.IsLittleEndian) Array.Reverse(bytes); + ms.Write(bytes); + } + } } } \ No newline at end of file diff --git a/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs b/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs new file mode 100644 index 0000000..3fae783 --- /dev/null +++ b/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs @@ -0,0 +1,189 @@ +using System; +using System.IO; +using Casper.Network.SDK.Types; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Casper.Network.SDK.ByteSerializers +{ + public class TransactionV1ByteSerializer : BaseByteSerializer, IByteSerializer + { + public byte[] ToBytes(TransactionTarget source) + { + var ms = new MemoryStream(); + + WriteByte(ms, (byte)source.Type); + + if (source.Type == TransactionTargetType.Stored) + { + switch (source.Id) + { + case ByHashInvocationTarget byHash: + WriteByte(ms, (byte)InvocationTargetTag.ByHash); + WriteBytes(ms, Hex.Decode(byHash.Hash)); + break; + case ByNameInvocationTarget byName: + WriteByte(ms, (byte)InvocationTargetTag.ByName); + WriteString(ms, byName.Name); + break; + case ByPackageHashInvocationTarget byPackageHash: + WriteByte(ms, (byte)InvocationTargetTag.ByPackageHash); + WriteBytes(ms, Hex.Decode(byPackageHash.Addr)); + WriteMaybeUInteger(ms, byPackageHash.Version); + break; + case ByPackageNameInvocationTarget byPackageName: + WriteByte(ms, (byte)InvocationTargetTag.ByPackageName); + WriteString(ms, byPackageName.Name); + WriteMaybeUInteger(ms, byPackageName.Version); + break; + } + WriteByte(ms, (byte)source.Runtime); + } + else if (source.Type == TransactionTargetType.Session) + { + WriteByte(ms, (byte)source.SessionKind); + if (source.ModuleBytes == null || source.ModuleBytes.Length == 0) + WriteInteger(ms, 0); + else + { + WriteInteger(ms, source.ModuleBytes.Length); + WriteBytes(ms, source.ModuleBytes); + } + + WriteByte(ms, (byte)source.Runtime); + } + + return ms.ToArray(); + } + + public byte[] ToBytes(TransactionEntryPoint source) + { + var ms = new MemoryStream(); + + if (source.Custom != null) + { + WriteByte(ms, 0x00); + WriteString(ms, source.Custom); + } + else if (source.Native.HasValue) + { + WriteByte(ms, (byte)source.Native.Value); + } + else + { + throw new Exception("Cannot serialize empty TransactionEntryPoint to bytes"); + } + + return ms.ToArray(); + } + + public byte[] ToBytes(TransactionScheduling source) + { + var ms = new MemoryStream(); + + WriteByte(ms, (byte)source.Type); + + if (source.Type == TransactionSchedulingType.FutureEra) + WriteULong(ms, source.EraId); + if (source.Type == TransactionSchedulingType.FutureTimestamp) + WriteULong(ms, source.Timestamp); + + return ms.ToArray(); + } + + public byte[] ToBytes(TransactionV1Body source) + { + var ms = new MemoryStream(); + + var namedArgSerializer = new NamedArgByteSerializer(); + + ms.Write(BitConverter.GetBytes(source.RuntimeArgs.Count)); + foreach (var args in source.RuntimeArgs) + WriteBytes(ms, namedArgSerializer.ToBytes(args)); + + WriteBytes(ms, ToBytes(source.Target)); + WriteBytes(ms, ToBytes(source.EntryPoint)); + WriteBytes(ms, ToBytes(source.Scheduling)); + + return ms.ToArray(); + } + + public byte[] ToBytes(PricingMode source) + { + var ms = new MemoryStream(); + + if (source.Type == PricingModeType.Classic && + source.PaymentAmount.HasValue && + source.GasPriceTolerance.HasValue && + source.StandardPayment.HasValue) + { + ms.WriteByte((byte)PricingModeType.Classic); + WriteULong(ms, (ulong)source.PaymentAmount.Value); + WriteByte(ms, (byte)source.GasPriceTolerance.Value); + WriteByte(ms, (byte)(source.StandardPayment.Value ? 0x01 : 0x00)); + } + else if (source.Type == PricingModeType.Fixed && + source.GasPriceTolerance.HasValue) + { + ms.WriteByte((byte)PricingModeType.Fixed); + WriteByte(ms, (byte)source.GasPriceTolerance.Value); + } + else if (source.Type == PricingModeType.Reserved && + source.Receipt != null) + { + ms.WriteByte((byte)PricingModeType.Reserved); + WriteBytes(ms, Hex.Decode(source.Receipt)); + } + + return ms.ToArray(); + } + + public byte[] ToBytes(InitiatorAddr source) + { + var ms = new MemoryStream(); + + if (source.PublicKey != null) + { + WriteByte(ms, 0x00); + WriteBytes(ms, source.PublicKey.GetBytes()); + } + else if (source.AccountHash != null) + { + WriteByte(ms, 0x01); + WriteBytes(ms, source.AccountHash.RawBytes); + } + + return ms.ToArray(); + } + + public byte[] ToBytes(TransactionV1Header source) + { + var ms = new MemoryStream(); + WriteString(ms, source.ChainName); + WriteULong(ms, source.Timestamp); + WriteULong(ms, source.Ttl); + WriteBytes(ms, Hex.Decode(source.BodyHash)); + WriteBytes(ms, ToBytes(source.PricingMode)); + WriteBytes(ms, ToBytes(source.InitiatorAddr)); + return ms.ToArray(); + } + + public byte[] ToBytes(TransactionV1 source) + { + var ms = new MemoryStream(); + + WriteBytes(ms, Hex.Decode(source.Hash)); + + WriteBytes(ms, ToBytes(source.Header)); + + WriteBytes(ms, ToBytes(source.Body)); + // add the approvals + // + var approvalSerializer = new DeployApprovalByteSerializer(); + WriteInteger(ms, source.Approvals.Count); + foreach (var approval in source.Approvals) + WriteBytes(ms, approvalSerializer.ToBytes(approval)); + + return ms.ToArray(); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/JsonRpc/CasperMethods.cs b/Casper.Network.SDK/JsonRpc/CasperMethods.cs index 7fedd10..e818468 100644 --- a/Casper.Network.SDK/JsonRpc/CasperMethods.cs +++ b/Casper.Network.SDK/JsonRpc/CasperMethods.cs @@ -337,6 +337,62 @@ public GetDeploy(string deployHash, bool finalizedApprovals = false) : base("inf this.Parameters.Add("finalized_approvals", true); } } + + + public class PutTransaction : RpcMethod + { + /// + /// Sends a Transaction to the network for its execution. + /// + /// The deploy object. + public PutTransaction(TransactionV1 transaction) : base("account_put_transaction") + { + this.Parameters = new Dictionary + { + { + "transaction", new Dictionary + { + { "Version1", transaction}, + } + } + }; + } + } + public class GetTransaction : RpcMethod + { + public GetTransaction(string deployHash, bool finalizedApprovals = false) : base("info_get_transaction") + { + this.Parameters = new Dictionary + { + { + "transaction_hash", new Dictionary + { + { "Deploy", deployHash } + } + }, + }; + if (finalizedApprovals) + this.Parameters.Add("finalized_approvals", true); + } + + public GetTransaction(TransactionHash transactionHash, bool finalizedApprovals = false) : base("info_get_transaction") + { + var hashDict = new Dictionary(); + if(transactionHash.Deploy != null) + hashDict.Add("Deploy", transactionHash.Deploy); + if(transactionHash.Version1 != null) + hashDict.Add("Version1", transactionHash.Version1); + + this.Parameters = new Dictionary + { + { + "transaction_hash", hashDict + }, + }; + if (finalizedApprovals) + this.Parameters.Add("finalized_approvals", true); + } + } public class GetBlock : RpcMethod { diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetTransactionResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetTransactionResult.cs new file mode 100644 index 0000000..500a6b5 --- /dev/null +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetTransactionResult.cs @@ -0,0 +1,35 @@ +using System.Text.Json.Serialization; +using Casper.Network.SDK.Types; + +namespace Casper.Network.SDK.JsonRpc.ResultTypes +{ + /// + /// Result for "info_get_deploy" RPC response. + /// + public class GetTransactionResult : RpcResult + { + /// + /// The deploy. + /// + [JsonPropertyName("transaction")] + public Transaction Transaction { get; init; } + + /// + /// The hash of the block in which the deploy was executed. + /// + [JsonPropertyName("block_hash")] + public string BlockHash { get; init; } + + /// + /// The height of the block in which the deploy was executed. + /// + [JsonPropertyName("block_height")] + public ulong BlockHeight { get; init; } + + /// + /// The execution result if known. + /// + [JsonPropertyName("execution_result")] + public ExecutionResult ExecutionResult { get; init; } + } +} diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/PutTransactionResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/PutTransactionResult.cs new file mode 100644 index 0000000..21de098 --- /dev/null +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/PutTransactionResult.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; +using Casper.Network.SDK.Types; + +namespace Casper.Network.SDK.JsonRpc.ResultTypes +{ + /// + /// Result for "account_put_transaction" RPC response. + /// + public class PutTransactionResult + { + /// + /// Hex-encoded transaction hash. + /// + [JsonPropertyName("transaction_hash")] + public TransactionHash TransactionHash { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index b6fa8c2..d783cf0 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -529,6 +529,70 @@ public async Task> GetDeploy(string deployHash, throw new TaskCanceledException("GetDeploy operation canceled"); } + /// + /// Send a Transaction to the network for its execution. + /// + /// The transaction object. + /// Throws an exception if the transaction is not signed. + public async Task> PutTransaction(TransactionV1 transaction) + { + if (transaction.Approvals.Count == 0) + throw new Exception("Sign the transaction before sending it to the network."); + + var method = new PutTransaction(transaction); + return await SendRpcRequestAsync(method); + } + + /// + /// Request a Transaction object from the network by the transaction (or deploy) hash. + /// When a cancellation token is included this method waits until the transaction is + /// executed, i.e. until the transaction contains the execution result information. + /// + /// An v1 transaction hash or a deploy hash + /// Whether to return the transaction with the finalized approvals + /// substituted. If `false` or omitted, returns the transaction with the approvals that were originally + /// received by the node. + /// A CancellationToken. Do not include this parameter to return + /// with the first transaction object returned by the network, even it's not executed. + /// The token has cancelled the operation before the deploy has been executed. + public async Task> GetTransaction(TransactionHash transactionHash, + bool finalizedApprovals = false, + CancellationToken cancellationToken = default(CancellationToken)) + { + var method = new GetTransaction(transactionHash, finalizedApprovals); + + while (!cancellationToken.IsCancellationRequested) + { + var response = await SendRpcRequestAsync(method); + if (!cancellationToken.CanBeCanceled || + response.Result.GetProperty("execution_result").GetArrayLength() > 0) + return response; + await Task.Delay(10000); + } + + throw new TaskCanceledException("GetDeploy operation canceled"); + } + + /// + /// Request a Transaction object from the network by the transaction hash. + /// When a cancellation token is included this method waits until the transaction is + /// executed, i.e. until the transaction contains the execution result information. + /// + /// A v1 transaction hash + /// Whether to return the transaction with the finalized approvals + /// substituted. If `false` or omitted, returns the transaction with the approvals that were originally + /// received by the node. + /// A CancellationToken. Do not include this parameter to return + /// with the first transaction object returned by the network, even it's not executed. + /// The token has cancelled the operation before the deploy has been executed. + public async Task> GetTransaction(string version1Hash, + bool finalizedApprovals = false, + CancellationToken cancellationToken = default(CancellationToken)) + { + return await this.GetTransaction(new TransactionHash { Version1 = version1Hash }, finalizedApprovals, + cancellationToken); + } + /// /// Retrieves a Block from the network by its hash. /// diff --git a/Casper.Network.SDK/Types/Deploy.cs b/Casper.Network.SDK/Types/Deploy.cs index af6850c..2ecde18 100644 --- a/Casper.Network.SDK/Types/Deploy.cs +++ b/Casper.Network.SDK/Types/Deploy.cs @@ -6,7 +6,6 @@ using System.Text.Json.Serialization; using Casper.Network.SDK.ByteSerializers; using Casper.Network.SDK.Converters; -using Casper.Network.SDK.JsonRpc; using Casper.Network.SDK.Utils; using Org.BouncyCastle.Utilities.Encoders; diff --git a/Casper.Network.SDK/Types/PricingMode.cs b/Casper.Network.SDK/Types/PricingMode.cs new file mode 100644 index 0000000..0ef5b1a --- /dev/null +++ b/Casper.Network.SDK/Types/PricingMode.cs @@ -0,0 +1,159 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ +public enum PricingModeType + { + /// + /// The original payment model, where the creator of the transaction specifies how much they will pay, + /// at what gas price. + /// + Classic = 0, + + /// + /// The cost of the transaction is determined by the cost table, per the transaction kind. + /// + Fixed = 1, + + /// + /// The payment for this transaction was previously reserved, as proven by the receipt hash + /// (this is for future use, not currently supported by the Casper network). + /// + Reserved = 2, + } + + /// + /// Pricing mode of a Transaction. + /// + public class PricingMode + { + /// + /// Pricing mode used: Classic, Fixed, Reserved. + /// + /// + public PricingModeType Type { get; init; } + + /// + /// Payment amount. + /// + public UInt64? PaymentAmount { get; set; } + + /// + /// User-specified gas_price tolerance (minimum 1). This is interpreted to mean "do not include this + /// transaction in a block if the current gas price is greater than this number". + /// + public UInt16? GasPriceTolerance { get; set; } + + /// + /// Standard payment. + /// + public bool? StandardPayment { get; set; } + + /// + /// Pre-paid receipt in the Reserved Pricing mode. + /// + public string Receipt { get; init; } + + public class PricingModeConverter : JsonConverter + { + public override PricingMode Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException("Cannot deserialize PricingMode. StartObject expected"); + reader.Read(); + + if (reader.TokenType != JsonTokenType.PropertyName) + throw new JsonException("Cannot deserialize PricingMode. PropertyName expected"); + + string pricingModeType = reader.GetString(); + reader.Read(); + + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException("Cannot deserialize PricingMode. StartObject expected"); + reader.Read(); + + UInt64? paymentAmount = null; + UInt16? gasPriceTolerance = null; + bool? standardPayment = null; + string receipt = null; + + while (reader.TokenType == JsonTokenType.PropertyName) + { + var field = reader.GetString(); + reader.Read(); + switch (field) + { + case "payment_amount": + paymentAmount = reader.GetUInt64(); + break; + case "standard_payment": + standardPayment = reader.GetBoolean(); + break; + case "gas_price_tolerance": + gasPriceTolerance = reader.GetUInt16(); + break; + case "receipt": + receipt = reader.GetString(); + break; + } + + reader.Read(); + } + + return new PricingMode() + { + Type = EnumCompat.Parse(pricingModeType), + PaymentAmount = paymentAmount, + GasPriceTolerance = gasPriceTolerance, + StandardPayment = standardPayment, + Receipt = receipt, + }; + } + + public override void Write( + Utf8JsonWriter writer, + PricingMode value, + JsonSerializerOptions options) + { + if (value.Type == PricingModeType.Classic) + { + writer.WriteStartObject(); + writer.WritePropertyName("Classic"); + writer.WriteStartObject(); + if (value.PaymentAmount.HasValue) + writer.WriteNumber("payment_amount", value.PaymentAmount.Value); + if (value.GasPriceTolerance.HasValue) + writer.WriteNumber("gas_price_tolerance", value.GasPriceTolerance.Value); + if (value.StandardPayment.HasValue) + writer.WriteBoolean("standard_payment", value.StandardPayment.Value); + writer.WriteEndObject(); + writer.WriteEndObject(); + } + else if (value.Type == PricingModeType.Fixed) + { + writer.WriteStartObject(); + writer.WritePropertyName("Fixed"); + writer.WriteStartObject(); + if (value.GasPriceTolerance.HasValue) + writer.WriteNumber("gas_price_tolerance", value.GasPriceTolerance.Value); + writer.WriteEndObject(); + writer.WriteEndObject(); + } + else if (value.Type == PricingModeType.Reserved) + { + writer.WriteStartObject(); + writer.WritePropertyName("Reserved"); + writer.WriteStartObject(); + writer.WriteString("receipt", value.Receipt); + writer.WriteEndObject(); + writer.WriteEndObject(); + } + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Transaction.cs b/Casper.Network.SDK/Types/Transaction.cs new file mode 100644 index 0000000..1334978 --- /dev/null +++ b/Casper.Network.SDK/Types/Transaction.cs @@ -0,0 +1,18 @@ +namespace Casper.Network.SDK.Types +{ + /// + /// A versioned wrapper for a transaction or deploy. + /// + public class Transaction + { + /// + /// The deploy. + /// + public Deploy Deploy { get; init; } + + /// + /// A version 1 transaction. + /// + public TransactionV1 TransactionV1 { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionEntryPoint.cs b/Casper.Network.SDK/Types/TransactionEntryPoint.cs new file mode 100644 index 0000000..ce812d0 --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionEntryPoint.cs @@ -0,0 +1,110 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + public enum NativeEntryPoint + { + /// + /// The `transfer` native entry point, used to transfer `Motes` from a source purse to a target purse. + /// + Transfer = 1, + /// + /// The `add_bid` native entry point, used to create or top off a bid purse. + /// + AddBid = 2, + /// + /// The `withdraw_bid` native entry point, used to decrease a stake. + /// + WithdrawBid = 3, + /// + /// The `delegate` native entry point, used to add a new delegator or increase an existing delegator's stake. + /// + Delegate = 4, + /// + /// The `undelegate` native entry point, used to reduce a delegator's stake or remove the delegator if the remaining stake is 0. + /// + Undelegate = 5, + /// + /// The `redelegate` native entry point, used to reduce a delegator's stake or remove the delegator if + /// the remaining stake is 0, and after the unbonding delay, automatically delegate to a new validator. + /// + Redelegate = 6, + /// + /// The `activate_bid` native entry point, used to used to reactivate an inactive bid. + /// + ActivateBid = 7, + } + + public class TransactionEntryPoint + { + public NativeEntryPoint? Native { get; init; } + + public string Custom { get; init; } + + public TransactionEntryPoint(NativeEntryPoint name) + { + Native = name; + Custom = null; + } + + public TransactionEntryPoint(string customEntryPoint) + { + Native = null; + Custom = customEntryPoint; + } + + public class TransactionEntryPointConverter : JsonConverter + { + public override TransactionEntryPoint Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.PropertyName) + { + var nativeEntryPoint = EnumCompat.Parse(reader.GetString()); + reader.Read(); + return new TransactionEntryPoint(nativeEntryPoint); + } + + if (reader.TokenType == JsonTokenType.StartObject) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.PropertyName && + reader.GetString() == "Custom") + { + reader.Read(); + var customEntryPoint = reader.GetString(); + reader.Read(); + reader.Read(); // read end object + return new TransactionEntryPoint(customEntryPoint); + } + } + + throw new JsonException("Cannot deserialize TransactionEntryPoint."); + } + + public override void Write( + Utf8JsonWriter writer, + TransactionEntryPoint value, + JsonSerializerOptions options) + { + if (value.Native.HasValue) + { + writer.WriteStringValue(value.Native.Value.ToString()); + } + else if (value.Custom != null) + { + writer.WriteStartObject(); + writer.WritePropertyName("Custom"); + writer.WriteStringValue(value.Custom); + writer.WriteEndObject(); + } + else + throw new JsonException("Cannot serialize empty transaction entry point."); + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionHash.cs b/Casper.Network.SDK/Types/TransactionHash.cs index 7b5e26b..391a09e 100644 --- a/Casper.Network.SDK/Types/TransactionHash.cs +++ b/Casper.Network.SDK/Types/TransactionHash.cs @@ -11,4 +11,4 @@ public override string ToString() return Deploy ?? Version1; } } -} \ No newline at end of file +} diff --git a/Casper.Network.SDK/Types/TransactionScheduling.cs b/Casper.Network.SDK/Types/TransactionScheduling.cs new file mode 100644 index 0000000..f3ed51d --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionScheduling.cs @@ -0,0 +1,106 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Utils; + +namespace Casper.Network.SDK.Types +{ + public enum TransactionSchedulingType + { + Standard = 0, + FutureEra = 1, + FutureTimestamp = 2, + } + + public class TransactionScheduling + { + public TransactionSchedulingType Type { get; init; } + + public ulong EraId { get; init; } + + public ulong Timestamp { get; init; } + + public class TransactionSchedulingConverter : JsonConverter + { + public override TransactionScheduling Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + TransactionScheduling scheduling; + + if (reader.TokenType == JsonTokenType.String) + { + var schedulingType = reader.GetString(); + reader.Read(); + switch (schedulingType) + { + case "Standard": + scheduling = new TransactionScheduling + { + Type = TransactionSchedulingType.Standard, + }; + break; + default: + throw new JsonException("Cannot deserialize TransactionScheduling. Unknown scheduling type"); + } + } + else if (reader.TokenType == JsonTokenType.StartObject) + { + reader.Read(); // skip start object + var schedulingType = reader.GetString(); + reader.Read(); + switch (schedulingType) + { + case "FutureEra": + scheduling = new TransactionScheduling + { + Type = TransactionSchedulingType.FutureEra, + EraId = reader.GetUInt64(), + }; + break; + case "FutureTimestamp": + scheduling = new TransactionScheduling + { + Type = TransactionSchedulingType.FutureTimestamp, + Timestamp = reader.GetUInt64(), + }; + break; + default: + throw new JsonException("Cannot deserialize TransactionScheduling. Unknown scheduling type"); + } + reader.Read(); // skip end object + } + else + throw new JsonException("Cannot deserialize TransactionScheduling."); + + return scheduling; + } + + public override void Write( + Utf8JsonWriter writer, + TransactionScheduling value, + JsonSerializerOptions options) + { + switch (value.Type) + { + case TransactionSchedulingType.Standard: + writer.WriteStringValue("Standard"); + break; + case TransactionSchedulingType.FutureEra: + writer.WriteStartObject(); + writer.WriteNumber("FutureEra", value.EraId); + writer.WriteEndObject(); + break; + case TransactionSchedulingType.FutureTimestamp: + writer.WriteStartObject(); + writer.WriteString("FutureTimestamp", DateUtils.ToISOString(value.Timestamp)); + writer.WriteEndObject(); + break; + default: + throw new JsonException("Cannot serialize due to unkown transaction scheduling type."); + } + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionTarget.cs b/Casper.Network.SDK/Types/TransactionTarget.cs new file mode 100644 index 0000000..d7280ec --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionTarget.cs @@ -0,0 +1,395 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Casper.Network.SDK.Types +{ + public enum InvocationTargetTag + { + ByHash = 0, + ByName = 1, + ByPackageHash = 2, + ByPackageName = 3, + } + + public class ByHashInvocationTarget : IInvocationTarget + { + public string Hash { get; init; } + } + + public class ByNameInvocationTarget : IInvocationTarget + { + public string Name { get; init; } + } + + public class ByPackageHashInvocationTarget : IInvocationTarget + { + [JsonPropertyName("addr")] public string Addr { get; init; } + + [JsonPropertyName("version")] public UInt32? Version { get; init; } + } + + public class ByPackageNameInvocationTarget : IInvocationTarget + { + [JsonPropertyName("name")] public string Name { get; init; } + + [JsonPropertyName("version")] public UInt32? Version { get; init; } + } + + [JsonConverter(typeof(InvocationTargetConverter))] + public interface IInvocationTarget + { + public class InvocationTargetConverter : JsonConverter + { + public override IInvocationTarget Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + IInvocationTarget target; + + if (reader.TokenType == JsonTokenType.StartObject) + { + reader.Read(); // skip start object + var idType = reader.GetString(); + reader.Read(); + switch (idType) + { + case "ByHash": + target = new ByHashInvocationTarget { Hash = reader.GetString() }; + reader.Read(); + break; + case "ByName": + target = new ByNameInvocationTarget { Name = reader.GetString() }; + reader.Read(); + break; + case "ByPackageHash": + target = JsonSerializer.Deserialize(ref reader, options); + break; + case "ByPackageName": + target = JsonSerializer.Deserialize(ref reader, options); + break; + default: + throw new JsonException( + "Cannot deserialize TransactionScheduling. Unknown scheduling type"); + } + + reader.Read(); // skip end object + } + else + throw new JsonException("Cannot deserialize TransactionScheduling."); + + return target; + } + + public override void Write( + Utf8JsonWriter writer, + IInvocationTarget value, + JsonSerializerOptions options) + { + writer.WriteStartObject(); + + if (value is ByHashInvocationTarget byHash) + writer.WriteString("ByHash", byHash.Hash); + else if (value is ByNameInvocationTarget byName) + writer.WriteString("ByName", byName.Name); + else if (value is ByPackageHashInvocationTarget byPackageHash) + { + writer.WritePropertyName("ByPackageHash"); + JsonSerializer.Serialize(writer, byPackageHash); + } + else if (value is ByPackageNameInvocationTarget byPackageName) + { + writer.WritePropertyName("ByPackageName"); + JsonSerializer.Serialize(writer, byPackageName); + } + else + throw new JsonException("Unknown invocation target type."); + + writer.WriteEndObject(); + } + } + } + + public enum TransactionTargetType + { + Native = 0, + Stored = 1, + Session = 2, + } + + public enum TransactionRuntime + { + /// + /// The Casper Version 1 Virtual Machine. + /// + VmCasperV1, + + /// + /// The Casper Version 2 Virtual Machine. + /// + VmCasperV2, + } + + public enum TransactionSessionKind + { + /// + /// A standard (non-special-case) session. + /// This kind of session is not allowed to install or upgrade a stored contract, + /// but can call stored contracts. + /// + Standard, + + /// + /// A session which installs a stored contract. + /// + Installer, + + /// + /// A session which upgrades a previously-installed stored contract. + /// Such a session must have \"package_id: PackageIdentifier\" runtime arg present. + /// + Upgrader, + + /// + /// A session which doesn't call any stored contracts. + /// This kind of session is not allowed to install or upgrade a stored contract. + /// + Isolated, + } + + public class TransactionTarget + { + public TransactionTargetType Type { get; init; } + + [JsonPropertyName("id")] public IInvocationTarget Id { get; init; } + + public TransactionSessionKind SessionKind { get; init; } + + /// + /// wasm Bytes + /// + [JsonPropertyName("module_bytes")] + [JsonConverter(typeof(HexBytesConverter))] + public byte[] ModuleBytes { get; init; } + + [JsonPropertyName("runtime")] public TransactionRuntime Runtime { get; set; } + + public static TransactionTarget StoredByHash(string hash) + { + return new TransactionTarget() + { + Type = TransactionTargetType.Stored, + Id = new ByHashInvocationTarget { Hash = hash } + }; + } + + public static TransactionTarget StoredByName(string name) + { + return new TransactionTarget() + { + Type = TransactionTargetType.Stored, + Id = new ByNameInvocationTarget { Name = name } + }; + } + + public static TransactionTarget StoredByPackageHash(string hash, UInt32? version = null) + { + return new TransactionTarget() + { + Type = TransactionTargetType.Stored, + Id = new ByPackageHashInvocationTarget { Addr = hash, Version = version } + }; + } + + public static TransactionTarget StoredByPackageName(string name, UInt32? version = null) + { + return new TransactionTarget() + { + Type = TransactionTargetType.Stored, + Id = new ByPackageNameInvocationTarget() { Name = name, Version = version } + }; + } + + public static TransactionTarget StandardSession(byte[] moduleBytes) + { + return new TransactionTarget() + { + Type = TransactionTargetType.Session, + SessionKind = TransactionSessionKind.Standard, + ModuleBytes = moduleBytes, + }; + } + + public static TransactionTarget InstallerSession(byte[] moduleBytes) + { + return new TransactionTarget() + { + Type = TransactionTargetType.Session, + SessionKind = TransactionSessionKind.Installer, + ModuleBytes = moduleBytes, + }; + } + + public static TransactionTarget UpgraderSession(byte[] moduleBytes) + { + return new TransactionTarget() + { + Type = TransactionTargetType.Session, + SessionKind = TransactionSessionKind.Upgrader, + ModuleBytes = moduleBytes, + }; + } + + public static TransactionTarget IsolatedSession(byte[] moduleBytes) + { + return new TransactionTarget() + { + Type = TransactionTargetType.Session, + SessionKind = TransactionSessionKind.Isolated, + ModuleBytes = moduleBytes, + }; + } + + public class TransactionTargetConverter : JsonConverter + { + public override TransactionTarget Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.PropertyName) + { + var targetType = reader.GetString(); + reader.Read(); + + var type = EnumCompat.Parse(targetType); + switch (targetType) + { + case "Native": + return new TransactionTarget + { + Type = TransactionTargetType.Native, + }; + default: + throw new JsonException($"TransactionTargetType '{targetType}' not supported."); + } + } + else if (reader.TokenType == JsonTokenType.StartObject) + { + TransactionTarget transactionTarget = null; + IInvocationTarget id = null; + string kind = null; + string module_bytes = null; + string runtime = null; + + reader.Read(); // skip start object + var targetType = reader.GetString(); + reader.Read(); + + switch (targetType) + { + case "Stored": + reader.Read(); + while (reader.TokenType != JsonTokenType.EndObject) + { + var prop = reader.GetString(); + reader.Read(); + switch (prop) + { + case "id": + id = JsonSerializer.Deserialize(ref reader, options); + break; + case "runtime": + runtime = reader.GetString(); + break; + } + } + + reader.Read(); // skip end object + transactionTarget = new TransactionTarget() + { + Type = EnumCompat.Parse(targetType), + Id = id, + Runtime = EnumCompat.Parse(runtime), + }; + break; + case "Session": + reader.Read(); + while (reader.TokenType != JsonTokenType.EndObject) + { + var prop = reader.GetString(); + reader.Read(); + switch (prop) + { + case "kind": + kind = reader.GetString(); + break; + case "module_bytes": + module_bytes = reader.GetString(); + break; + case "runtime": + runtime = reader.GetString(); + break; + } + } + + reader.Read(); // skip end object + transactionTarget = new TransactionTarget() + { + Type = EnumCompat.Parse(targetType), + SessionKind = EnumCompat.Parse(kind), + ModuleBytes = Hex.Decode(module_bytes), + Runtime = EnumCompat.Parse(runtime), + }; + break; + default: + throw new JsonException($"TransactionTargetType '{targetType}' not supported."); + } + + reader.Read(); // skip end object + + return transactionTarget; + } + + throw new JsonException("Cannot deserialize TransactionTarget. PropertyName expected"); + } + + public override void Write( + Utf8JsonWriter writer, + TransactionTarget value, + JsonSerializerOptions options) + { + switch (value.Type) + { + case TransactionTargetType.Native: + writer.WriteStringValue("Native"); + break; + case TransactionTargetType.Stored: + writer.WriteStartObject(); + writer.WriteStartObject("Stored"); + writer.WritePropertyName("id"); + JsonSerializer.Serialize(writer, value.Id); + writer.WriteString("runtime", value.Runtime.ToString()); + writer.WriteEndObject(); + writer.WriteEndObject(); + break; + case TransactionTargetType.Session: + writer.WriteStartObject(); + writer.WriteStartObject("Session"); + writer.WriteString("kind", value.SessionKind.ToString()); + writer.WriteString("module_bytes", Hex.ToHexString(value.ModuleBytes)); + writer.WriteString("runtime", value.Runtime.ToString()); + writer.WriteEndObject(); + writer.WriteEndObject(); + break; + default: + throw new JsonException("Cannot serialize empty transaction target."); + } + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionV1.cs b/Casper.Network.SDK/Types/TransactionV1.cs new file mode 100644 index 0000000..e953275 --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionV1.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using Casper.Network.SDK.ByteSerializers; +using Casper.Network.SDK.Utils; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Casper.Network.SDK.Types +{ + /// + /// A unit of work sent by a client to the network, which when executed can cause global state to be altered. + /// + public class TransactionV1 + { + /// + /// A hash over the header of the transaction. + /// + [JsonPropertyName("hash")] + [JsonConverter(typeof(CEP57Checksum.HashWithChecksumConverter))] + public string Hash { get; } + + /// + /// List of signers and signatures for this transaction. + /// + [JsonPropertyName("approvals")] + public List Approvals { get; } = new List(); + + /// + /// Header for this transaction. + /// + [JsonPropertyName("header")] + public TransactionV1Header Header { get; init; } + + /// + /// Body for this transaction. + /// + [JsonPropertyName("body")] + public TransactionV1Body Body { get; init; } + + /// + /// Loads and deserializes a TransactionV1 from a file. + /// + public static TransactionV1 Load(string filename) + { + var data = File.ReadAllText(filename); + return TransactionV1.Parse(data); + } + + /// + /// Parses a Transaction from a string with json. + /// + public static TransactionV1 Parse(string json) + { + try + { + var transaction = JsonSerializer.Deserialize(json); + + return transaction; + } + catch (JsonException e) + { + var message = $"The JSON value could not be converted to a TransactionV1 object. " + + $"{e.Message} Path: {e.Path} | LineNumber: {e.LineNumber} | " + + $"BytePositionInLine: {e.BytePositionInLine}."; + throw new Exception(message); + } + } + + /// + /// Saves a transaction object to a file. + /// + public void Save(string filename) + { + File.WriteAllText(filename, JsonSerializer.Serialize(this)); + } + + /// + /// Returns a json string with the transaction. + /// + public string SerializeToJson() + { + return JsonSerializer.Serialize(this); + } + + [JsonConstructor] + public TransactionV1(string hash, + TransactionV1Header header, + TransactionV1Body body, + List approvals) + { + this.Hash = hash; + this.Header = header; + this.Body = body; + this.Approvals = approvals; + } + + public TransactionV1(TransactionV1Header header, + TransactionV1Body body) + { + var bodyHash = ComputeBodyHash(body); + this.Header = new TransactionV1Header() + { + ChainName = header.ChainName, + Timestamp = header.Timestamp, + Ttl = header.Ttl, + BodyHash = Hex.ToHexString(bodyHash), + PricingMode = header.PricingMode, + InitiatorAddr = header.InitiatorAddr, + }; + this.Hash = Hex.ToHexString(ComputeHeaderHash(this.Header)); + this.Body = body; + } + + /// + /// Signs the transaction with a private key and adds a new Approval to it. + /// + public void Sign(KeyPair keyPair) + { + byte[] signature = keyPair.Sign(Hex.Decode(this.Hash)); + + Approvals.Add(new Approval() + { + Signature = Signature.FromRawBytes(signature, keyPair.PublicKey.KeyAlgorithm), + Signer = keyPair.PublicKey + }); + } + + /// + /// Adds an approval to the transaction. No check is done to the approval signature. + /// + public void AddApproval(Approval approval) + { + this.Approvals.Add(approval); + } + + /// + /// Validates the body and header hashes in the transaction. + /// + /// output string with a validation error message if validation fails. empty otherwise. + /// false if the validation of hashes is not successful + public bool ValidateHashes(out string message) + { + var computedHash = ComputeBodyHash(this.Body); + if (!Hex.Decode(this.Header.BodyHash).SequenceEqual(computedHash)) + { + message = "Computed Body Hash does not match value in transaction header. " + + $"Expected: '{this.Header.BodyHash}'. " + + $"Computed: '{computedHash}'."; + return false; + } + + computedHash = ComputeHeaderHash(this.Header); + if (!Hex.Decode(this.Hash).SequenceEqual(computedHash)) + { + message = "Computed Hash does not match value in transaction object. " + + $"Expected: '{this.Hash}'. " + + $"Computed: '{computedHash}'."; + return false; + } + + message = ""; + return true; + } + + /// + /// Verifies the signatures in the list of approvals. + /// + /// an output string with the signer which signature could not be verified. empty if verification succeeds. + /// false if the verification of a signature fails. + public bool VerifySignatures(out string message) + { + message = string.Empty; + + foreach (var approval in Approvals) + { + if (!approval.Signer.VerifySignature(Hex.Decode(this.Hash), + approval.Signature.RawBytes)) + { + message = $"Error verifying signature with signer '{approval.Signer}'."; + return false; + } + } + + return true; + } + + /// + /// returns the number of bytes resulting from the binary serialization of the Deploy. + /// + public int GetTransactionSizeInBytes() + { + var serializer = new TransactionV1ByteSerializer(); + return serializer.ToBytes(this).Length; + } + + private byte[] ComputeBodyHash(TransactionV1Body body) + { + var ms = new MemoryStream(); + + var serializer = new TransactionV1ByteSerializer(); + + ms.Write(serializer.ToBytes(body)); + + var bcBl2bdigest = new Org.BouncyCastle.Crypto.Digests.Blake2bDigest(256); + var bBody = ms.ToArray(); + + bcBl2bdigest.BlockUpdate(bBody, 0, bBody.Length); + + var hash = new byte[bcBl2bdigest.GetDigestSize()]; + bcBl2bdigest.DoFinal(hash, 0); + + return hash; + } + + private byte[] ComputeHeaderHash(TransactionV1Header header) + { + var serializer = new TransactionV1ByteSerializer(); + var bHeader = serializer.ToBytes(header); + + var bcBl2bdigest = new Org.BouncyCastle.Crypto.Digests.Blake2bDigest(256); + + bcBl2bdigest.BlockUpdate(bHeader, 0, bHeader.Length); + + var hash = new byte[bcBl2bdigest.GetDigestSize()]; + bcBl2bdigest.DoFinal(hash, 0); + + return hash; + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionV1Body.cs b/Casper.Network.SDK/Types/TransactionV1Body.cs new file mode 100644 index 0000000..ef99a09 --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionV1Body.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; + +namespace Casper.Network.SDK.Types +{ + /// + /// Body of a TransactionV1. + /// + public class TransactionV1Body + { + /// + /// List of runtime arguments. + /// + [JsonPropertyName("args")] + [JsonConverter(typeof(GenericListConverter))] + public List RuntimeArgs { get; init; } + + /// + /// Entry point or method of the contract to call. + /// + [JsonPropertyName("entry_point")] + [JsonConverter(typeof(TransactionEntryPoint.TransactionEntryPointConverter))] + public TransactionEntryPoint EntryPoint { get; init; } + + /// + /// Target of the transaction (native, custom or module_bytes). + /// + [JsonPropertyName("target")] + [JsonConverter(typeof(TransactionTarget.TransactionTargetConverter))] + public TransactionTarget Target { get; init; } + + /// + /// Scheduling of the transaction.. + /// + [JsonPropertyName("scheduling")] + [JsonConverter(typeof(TransactionScheduling.TransactionSchedulingConverter))] + public TransactionScheduling Scheduling { get; init; } + } +} diff --git a/Casper.Network.SDK/Types/TransactionV1Header.cs b/Casper.Network.SDK/Types/TransactionV1Header.cs new file mode 100644 index 0000000..f1cdf15 --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionV1Header.cs @@ -0,0 +1,54 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; +using Casper.Network.SDK.Utils; + +namespace Casper.Network.SDK.Types +{ + /// + /// The header portion of a TransactionV1. + /// + public class TransactionV1Header + { + /// + /// The address of the initiator of a transaction. + /// + [JsonPropertyName("initiator_addr")] + public InitiatorAddr InitiatorAddr { get; set; } + + /// + /// Timestamp formatted as per RFC 3339 + /// + [JsonPropertyName("timestamp")] + [JsonConverter(typeof(DateTime2EpochConverter))] + public ulong Timestamp { get; set; } + + /// + /// Duration of the Deploy in milliseconds (from timestamp). + /// + [JsonPropertyName("ttl")] + [JsonConverter(typeof(HumanizeTTLConverter))] + public ulong Ttl { get; set; } + + /// + /// Pricing mode of a Transaction. + /// + [JsonPropertyName("pricing_mode")] + [JsonConverter(typeof(PricingMode.PricingModeConverter))] + public PricingMode PricingMode { get; set; } + + /// + /// Hash of the body part of this Deploy. + /// + [JsonPropertyName("body_hash")] + [JsonConverter(typeof(CEP57Checksum.HashWithChecksumConverter))] + public string BodyHash { get; set; } + + /// + /// Name of the chain where the deploy is executed. + /// + [JsonPropertyName("chain_name")] + public string ChainName { get; set; } + } +} \ No newline at end of file From 6cedd43ef4612e7e9d944a35d8c50bafd6b819ce Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 3 Jun 2024 18:09:59 +0200 Subject: [PATCH 018/126] Updates to send transactions on rc2 Signed-off-by: David Hernando --- .../TransactionV1ByteSerializer.cs | 1 + Casper.Network.SDK/Types/BidKind.cs | 75 ++++++++++++++++++- .../Types/TransactionEntryPoint.cs | 8 ++ Casper.Network.SDK/Types/TransactionV1Body.cs | 3 + 4 files changed, 86 insertions(+), 1 deletion(-) diff --git a/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs b/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs index 3fae783..3bcc58c 100644 --- a/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs +++ b/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs @@ -102,6 +102,7 @@ public byte[] ToBytes(TransactionV1Body source) WriteBytes(ms, ToBytes(source.Target)); WriteBytes(ms, ToBytes(source.EntryPoint)); + WriteByte(ms, source.TransactionKind); WriteBytes(ms, ToBytes(source.Scheduling)); return ms.ToArray(); diff --git a/Casper.Network.SDK/Types/BidKind.cs b/Casper.Network.SDK/Types/BidKind.cs index 5b3a33c..24715a9 100644 --- a/Casper.Network.SDK/Types/BidKind.cs +++ b/Casper.Network.SDK/Types/BidKind.cs @@ -1,19 +1,92 @@ +using System.Numerics; using System.Text.Json.Serialization; namespace Casper.Network.SDK.Types { + /// + /// A bridge record pointing to a new `ValidatorBid` after the public key was changed. + /// + public class Bridge + { + + /// + /// Previous validator public key associated with the bid. + /// + [JsonPropertyName("old_validator_public_key")] + public PublicKey OldValidator { get; init; } + + /// + /// New validator public key associated with the bid. + /// + [JsonPropertyName("new_validator_public_key")] + public PublicKey NewValidator { get; init; } + + /// + /// Era when bridge record was created. + /// + [JsonPropertyName("era_id")] + public ulong EraId { get; init; } + + } + /// + /// Validator credit record. + /// + public class ValidatorCredit + { + /// + /// The credit amount. + /// + [JsonPropertyName("amount")] + public BigInteger Amount { get; init; } + + /// + /// The era id the credit was created. + /// + [JsonPropertyName("era_id")] + public ulong EraId { get; init; } + + /// + /// Validator public key. + /// + [JsonPropertyName("validator_public_key")] + public PublicKey Validator { get; init; } + } + /// /// Auction bid variants. /// public class BidKind { + /// + /// A unified record indexed on validator data, with an embedded collection of all delegator bids assigned + /// to that validator. The Unified variant is for legacy retrograde support, new instances will not be + /// created going forward. + /// [JsonPropertyName("Unified")] public Bid Unified { get; init; } + /// + /// A bid record containing only validator data. + /// [JsonPropertyName("Validator")] public Bid Validator { get; init; } + /// + /// A bid record containing only delegator data. + /// [JsonPropertyName("Delegator")] public Delegator Delegator { get; init; } + + /// + /// A bridge record pointing to a new `ValidatorBid` after the public key was changed. + /// + [JsonPropertyName("Bridge")] + public Bridge Bridge { get; init; } + + /// + /// Credited amount. + /// + [JsonPropertyName("Credit")] + public ValidatorCredit Credit { get; init; } } -} \ No newline at end of file +} diff --git a/Casper.Network.SDK/Types/TransactionEntryPoint.cs b/Casper.Network.SDK/Types/TransactionEntryPoint.cs index ce812d0..4dc9f54 100644 --- a/Casper.Network.SDK/Types/TransactionEntryPoint.cs +++ b/Casper.Network.SDK/Types/TransactionEntryPoint.cs @@ -35,6 +35,14 @@ public enum NativeEntryPoint /// The `activate_bid` native entry point, used to used to reactivate an inactive bid. /// ActivateBid = 7, + /// + /// The `change_bid_public_key` native entry point, used to change a bid's public key. + /// + ChangeBidPublicKey = 8, + /// + /// Used to call entry point call() in session transactions + /// + Call = 9, } public class TransactionEntryPoint diff --git a/Casper.Network.SDK/Types/TransactionV1Body.cs b/Casper.Network.SDK/Types/TransactionV1Body.cs index ef99a09..61e1748 100644 --- a/Casper.Network.SDK/Types/TransactionV1Body.cs +++ b/Casper.Network.SDK/Types/TransactionV1Body.cs @@ -36,5 +36,8 @@ public class TransactionV1Body [JsonPropertyName("scheduling")] [JsonConverter(typeof(TransactionScheduling.TransactionSchedulingConverter))] public TransactionScheduling Scheduling { get; init; } + + [JsonPropertyName("transaction_kind")] + public byte TransactionKind { get; init; } } } From c8d44590bc4730fe6140f08868326781b046d5d1 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 4 Jun 2024 09:19:56 +0200 Subject: [PATCH 019/126] fix URL for SSE endpoint Signed-off-by: David Hernando --- Casper.Network.SDK/SSE/ServerEventsClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Casper.Network.SDK/SSE/ServerEventsClient.cs b/Casper.Network.SDK/SSE/ServerEventsClient.cs index f3af59c..04b066a 100644 --- a/Casper.Network.SDK/SSE/ServerEventsClient.cs +++ b/Casper.Network.SDK/SSE/ServerEventsClient.cs @@ -276,7 +276,7 @@ private Task ListenChannelAsync(ChannelType channelType, int? startFrom, Cancell try { var uriBuilder = new UriBuilder(new Uri(client.BaseAddress + - $"events/{channelType.ToString().ToLowerInvariant()}")); + $"events")); if (startFrom != null && startFrom != int.MaxValue) uriBuilder.Query = $"start_from={startFrom}"; From 66a4a46a7596c26dddb586ef50c403f221147e18 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 5 Jun 2024 11:43:36 +0200 Subject: [PATCH 020/126] CSDK-181 add backwards compat to GetDeploy() RPC method Signed-off-by: David Hernando --- .../JsonRpc/ResultTypes/GetDeployResult.cs | 80 ++++++- .../ResultTypes/QueryGlobalStateResult.cs | 1 - .../ResultTypes/SpeculativeExecutionResult.cs | 2 +- Casper.Network.SDK/SSE/DeployProcessed.cs | 2 +- Casper.Network.SDK/Types/BidKind.cs | 5 + Casper.Network.SDK/Types/ExecutionInfo.cs | 1 + Casper.Network.SDK/Types/ExecutionResult.cs | 204 ++++++++++++++++-- .../Types/GlobalStateKey/BidAddrKey.cs | 11 +- .../Types/GlobalStateKey/GlobalStateKey.cs | 16 ++ Casper.Network.SDK/Types/StoredValue.cs | 4 +- .../Types/{TransformV2.cs => Transform.cs} | 117 +++++++--- Casper.Network.SDK/Types/TransformV1.cs | 4 +- 12 files changed, 394 insertions(+), 53 deletions(-) rename Casper.Network.SDK/Types/{TransformV2.cs => Transform.cs} (67%) diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs index 7ac65f1..9622793 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs @@ -1,13 +1,38 @@ +using System; using System.Collections.Generic; +using System.Text.Json; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; using Casper.Network.SDK.Types; namespace Casper.Network.SDK.JsonRpc.ResultTypes { + public class GetDeployResultCompat : RpcResult + { + /// + /// The deploy. + /// + [JsonPropertyName("deploy")] + public Deploy Deploy { get; init; } + + /// + /// Execution info, if available. + /// + [JsonPropertyName("execution_info")] + public ExecutionInfo ExecutionInfo { get; init; } + + /// + /// The map of block hash to execution result. + /// + [JsonPropertyName("execution_results")] + [JsonConverter(typeof(ExecutionResultV1.ExecutionResultV1Converter))] + public ExecutionResultV1 ExecutionResult { get; init; } + } + /// /// Result for "info_get_deploy" RPC response. /// + [JsonConverter(typeof(GetDeployResultConverter))] public class GetDeployResult : RpcResult { /// @@ -21,5 +46,58 @@ public class GetDeployResult : RpcResult /// [JsonPropertyName("execution_info")] public ExecutionInfo ExecutionInfo { get; init; } + + public class GetDeployResultConverter : JsonConverter + { + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert == typeof(GetDeployResult); + } + + public override GetDeployResult Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + var resultCompat = JsonSerializer.Deserialize(ref reader, options); + + if (resultCompat.ExecutionResult != null) + { + return new GetDeployResult() + { + ApiVersion = resultCompat.ApiVersion, + Deploy = resultCompat.Deploy, + ExecutionInfo = new ExecutionInfo + { + BlockHash = resultCompat.ExecutionResult.BlockHash, + BlockHeight = 0, + ExecutionResult = (ExecutionResult)resultCompat.ExecutionResult, + }, + }; + } + + return new GetDeployResult() + { + ApiVersion = resultCompat.ApiVersion, + Deploy = resultCompat.Deploy, + ExecutionInfo = resultCompat.ExecutionInfo, + }; + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + GetDeployResult block, + JsonSerializerOptions options) + { + throw new JsonException($"not yet implemented"); + } + } } -} +} \ No newline at end of file diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs index 6faaf6e..3b02e40 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs @@ -12,7 +12,6 @@ public class QueryGlobalStateResult : RpcResult /// The block header if a Block hash was provided. /// [JsonPropertyName("block_header")] - [JsonConverter(typeof(BlockHeader.BlockHeaderConverter))] public BlockHeader BlockHeader { get; init; } /// diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/SpeculativeExecutionResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/SpeculativeExecutionResult.cs index 21564a4..75efd7b 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/SpeculativeExecutionResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/SpeculativeExecutionResult.cs @@ -18,7 +18,7 @@ public class SpeculativeExecutionResult : RpcResult /// The result of executing the Deploy. /// [JsonPropertyName("execution_result")] - [JsonConverter(typeof(ExecutionResultV1.ExecutionResultConverter))] + [JsonConverter(typeof(ExecutionResultV1.ExecutionResultV1Converter))] public ExecutionResultV1 ExecutionResult { get; init; } } } diff --git a/Casper.Network.SDK/SSE/DeployProcessed.cs b/Casper.Network.SDK/SSE/DeployProcessed.cs index 4cfb7d0..904b8b3 100644 --- a/Casper.Network.SDK/SSE/DeployProcessed.cs +++ b/Casper.Network.SDK/SSE/DeployProcessed.cs @@ -52,7 +52,7 @@ public class DeployProcessed /// The result of executing a this Deploy. /// [JsonPropertyName("execution_result")] - [JsonConverter(typeof(ExecutionResultV1.ExecutionResultConverter))] + [JsonConverter(typeof(ExecutionResultV1.ExecutionResultV1Converter))] public ExecutionResultV1 ExecutionResult { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/BidKind.cs b/Casper.Network.SDK/Types/BidKind.cs index 24715a9..e88d68f 100644 --- a/Casper.Network.SDK/Types/BidKind.cs +++ b/Casper.Network.SDK/Types/BidKind.cs @@ -1,5 +1,6 @@ using System.Numerics; using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; namespace Casper.Network.SDK.Types { @@ -13,12 +14,14 @@ public class Bridge /// Previous validator public key associated with the bid. /// [JsonPropertyName("old_validator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] public PublicKey OldValidator { get; init; } /// /// New validator public key associated with the bid. /// [JsonPropertyName("new_validator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] public PublicKey NewValidator { get; init; } /// @@ -37,6 +40,7 @@ public class ValidatorCredit /// The credit amount. /// [JsonPropertyName("amount")] + [JsonConverter(typeof(BigIntegerConverter))] public BigInteger Amount { get; init; } /// @@ -49,6 +53,7 @@ public class ValidatorCredit /// Validator public key. /// [JsonPropertyName("validator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] public PublicKey Validator { get; init; } } diff --git a/Casper.Network.SDK/Types/ExecutionInfo.cs b/Casper.Network.SDK/Types/ExecutionInfo.cs index 29c04df..2553f47 100644 --- a/Casper.Network.SDK/Types/ExecutionInfo.cs +++ b/Casper.Network.SDK/Types/ExecutionInfo.cs @@ -20,6 +20,7 @@ public class ExecutionInfo /// The map of block hash to execution result. /// [JsonPropertyName("execution_result")] + [JsonConverter(typeof(ExecutionResult.ExecutionResultConverter))] public ExecutionResult ExecutionResult { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/ExecutionResult.cs b/Casper.Network.SDK/Types/ExecutionResult.cs index db844c2..237a17c 100644 --- a/Casper.Network.SDK/Types/ExecutionResult.cs +++ b/Casper.Network.SDK/Types/ExecutionResult.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Numerics; using System.Text.Json; using System.Text.Json.Serialization; @@ -7,6 +8,173 @@ namespace Casper.Network.SDK.Types { + public class ExecutionResult + { + protected int _version; + + /// + /// Returns the version of the block. + /// + public int Version + { + get { return _version; } + } + + protected ExecutionResultV1 _executionResultV1; + + public static explicit operator ExecutionResultV1(ExecutionResult executionResult) + { + if(executionResult._version == 1) + return executionResult._executionResultV1; + + throw new InvalidCastException("Version2 execution result cannot be converted to Version1"); + } + + public static explicit operator ExecutionResult(ExecutionResultV1 executionResult) + { + var v2Transfers = executionResult.Transfers.Select(key => + { + var transform = executionResult.Effect.Transforms.FirstOrDefault(tr => tr.Key.Equals(key)); + if (transform != null && transform.Kind == TransformKindV1.WriteTransfer) + return (Transfer)transform.Value; + return null; + }).ToList(); + + var v2Effect = executionResult.Effect.Transforms.Select(t => (Transform)t).ToList(); + + return new ExecutionResult + { + _version = 1, + _executionResultV1 = executionResult, + ErrorMessage = executionResult.ErrorMessage, + // Transfers = executionResult.Transfers, + Cost = executionResult.Cost, + Limit = 0, + Consumed = 0, + Effect = v2Effect, + SizeEstimate = 0, + Transfers = v2Transfers, + }; + } + + /// + /// What was the maximum allowed gas limit for this transaction?. + /// + [JsonPropertyName("limit")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Limit { get; init; } + + /// + /// How much gas was consumed executing this transaction. + /// + [JsonPropertyName("consumed")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Consumed { get; init; } + + /// + /// How much was paid for this transaction. + /// + [JsonPropertyName("cost")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Cost { get; init; } + + /// + /// If there is no error message, this execution was processed successfully. If there is an error message, this + /// execution failed to fully process for the stated reason. + /// + [JsonPropertyName("error_message")] + public string ErrorMessage { get; init; } + + /// + /// A record of transfers performed while executing this transaction. + /// + [JsonPropertyName("transfers")] + public List Transfers { get; init; } + + /// + /// The size estimate of the transaction + /// + [JsonPropertyName("size_estimate")] + public UInt64 SizeEstimate { get; init; } + + /// + /// A log of all transforms produced during execution. + /// + [JsonPropertyName("effects")] + [JsonConverter(typeof(GenericListConverter))] + public List Effect { get; init; } + + /// + /// Json converter class to serialize/deserialize a ExecutionResult to/from Json + /// + public class ExecutionResultConverter : JsonConverter + { + public override ExecutionResult Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + reader.Read(); + var version = reader.GetString(); + reader.Read(); + switch (version) + { + case "Version1": + var erV1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return (ExecutionResult)erV1; + case "Version2": + var erv2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new ExecutionResult + { + _version = 2, + ErrorMessage = erv2.ErrorMessage, + Transfers = erv2.Transfers, + Cost = erv2.Cost, + Limit = erv2.Limit, + Consumed = erv2.Consumed, + Effect = erv2.Effect, + SizeEstimate = erv2.SizeEstimate, + }; + default: + throw new JsonException("Expected Version1 or Version2"); + } + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + ExecutionResult executionResult, + JsonSerializerOptions options) + { + switch (executionResult.Version) + { + case 1: + writer.WritePropertyName("Version1"); + writer.WriteStartObject(); + JsonSerializer.Serialize((ExecutionResultV1)executionResult, options); + writer.WriteEndObject(); + break; + case 2: + writer.WritePropertyName("Version2"); + writer.WriteStartObject(); + JsonSerializer.Serialize(executionResult, options); + writer.WriteEndObject(); + break; + default: + throw new JsonException($"Unexpected execution result version {executionResult.Version}"); + } + } + } + } + /// /// The result of executing a single deploy. /// @@ -50,13 +218,18 @@ public ExecutionResultV1() Transfers = new List(); } - public class ExecutionResultConverter : JsonConverter + public class ExecutionResultV1Converter : JsonConverter { public override ExecutionResultV1 Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + var firstTokenType = reader.TokenType; + + if(firstTokenType == JsonTokenType.StartArray) //skip start array (backward compat) + reader.Read(); + if (reader.TokenType == JsonTokenType.StartObject) reader.Read(); @@ -99,6 +272,12 @@ public override ExecutionResultV1 Read( break; } } + + if(firstTokenType == JsonTokenType.StartArray) + reader.Read(); // skip end object + + // if (reader.TokenType == JsonTokenType.EndArray) + // reader.Read(); //skip end array (backward compat) if (executionResult == null) throw new JsonException("Could not deserialize ExecutionResult object"); @@ -123,8 +302,8 @@ public override void Write( } } } - - public class ExecutionResultV2 + + internal class ExecutionResultV2 { /// /// What was the maximum allowed gas limit for this transaction?. @@ -170,20 +349,7 @@ public class ExecutionResultV2 /// A log of all transforms produced during execution. /// [JsonPropertyName("effects")] - [JsonConverter(typeof(GenericListConverter))] - public List Effect { get; init; } - } - - public class ExecutionResult - { - /// - /// Version 1 of execution result type. - /// - public ExecutionResultV1 Version1 { get; init; } - - /// - /// Version 2 of execution result type. - /// - public ExecutionResultV2 Version2 { get; init; } + [JsonConverter(typeof(GenericListConverter))] + public List Effect { get; init; } } -} \ No newline at end of file +} diff --git a/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs index 0ab3d81..49e454c 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs @@ -18,6 +18,10 @@ public enum BidAddrTag /// Delegator BidAddr, /// Delegator = 2, + /// + /// BidAddr for auction credit. + /// + Credit = 4, } public class BidAddrKey : GlobalStateKey @@ -49,7 +53,7 @@ public AccountHashKey Delegator public BidAddrKey(string key) : base(key, KEYPREFIX) { KeyIdentifier = KeyIdentifier.BidAddr; - var bytes = Hex.Decode(Key); + var bytes = Hex.Decode(key.Substring(key.LastIndexOf('-') + 1)); if (bytes.Length <= 0) throw new Exception("Wrong key length."); switch (bytes[0]) @@ -69,6 +73,11 @@ public BidAddrKey(string key) : base(key, KEYPREFIX) if (bytes.Length != 65) throw new Exception("Wrong key length for Unified BidAddr. Expected 65 bytes."); break; + case (byte)BidAddrTag.Credit: + Tag = BidAddrTag.Credit; + if (bytes.Length != 41) + throw new Exception("Wrong key length for Credit BidAddr. Expected 41 bytes."); + break; default: throw new Exception($"Wrong BidAddr tag '{bytes[0]}'."); } diff --git a/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs index 0409232..bc99b49 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs @@ -269,6 +269,22 @@ public override string ToString() return Key; } + public override bool Equals(object obj) + { + //Check for null and compare run-time types. + if ((obj == null) || ! this.GetType().Equals(obj.GetType())) + { + return false; + } + + return Key.ToLowerInvariant().Equals(((GlobalStateKey) obj).Key.ToLowerInvariant()); + } + + public override int GetHashCode() + { + return Key.ToLowerInvariant().GetHashCode(); + } + /// /// Json converter class to serialize/deserialize an object derived from /// GlobalStateKey to/from Json diff --git a/Casper.Network.SDK/Types/StoredValue.cs b/Casper.Network.SDK/Types/StoredValue.cs index 47e33bd..69c174a 100644 --- a/Casper.Network.SDK/Types/StoredValue.cs +++ b/Casper.Network.SDK/Types/StoredValue.cs @@ -22,13 +22,15 @@ public class StoredValue public ContractPackage ContractPackage { get; init; } - public TransferV1 LegacyTransfer { get; init; } + public Transfer LegacyTransfer { get; init; } public DeployInfo DeployInfo { get; init; } public EraInfo EraInfo { get; init; } public Bid Bid { get; init; } + + public BidKind BidKind { get; init; } public List Withdraw { get; init; } diff --git a/Casper.Network.SDK/Types/TransformV2.cs b/Casper.Network.SDK/Types/Transform.cs similarity index 67% rename from Casper.Network.SDK/Types/TransformV2.cs rename to Casper.Network.SDK/Types/Transform.cs index abd4478..931ca2f 100644 --- a/Casper.Network.SDK/Types/TransformV2.cs +++ b/Casper.Network.SDK/Types/Transform.cs @@ -1,53 +1,63 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Numerics; using System.Text.Json; using System.Text.Json.Serialization; namespace Casper.Network.SDK.Types { - public enum TransformKindV2 + public enum TransformKind { /// /// An identity transformation that does not modify a value in the global state. Created as a result of /// reading from the global state. /// Identity, + /// /// Writes a new value (StoredValue) in the global state. /// Write, + /// /// A wrapping addition of an `i32` to an existing numeric value (not necessarily an `i32`) in the global state. /// AddInt32, + /// /// A wrapping addition of a `u64` to an existing numeric value (not necessarily an `u64`) in the global state. /// AddUInt64, + /// /// A wrapping addition of a `U128` to an existing numeric value (not necessarily an `U128`) in the global state. /// AddUInt128, + /// /// A wrapping addition of a `U256` to an existing numeric value (not necessarily an `U256`) in the global state. /// AddUInt256, + /// /// A wrapping addition of a `U512` to an existing numeric value (not necessarily an `U512`) in the global state. /// AddUInt512, + /// /// Adds new named keys to an existing entry in the global state.\n\nThis transform assumes that the existing stored /// value is either an Account or a Contract. /// AddKeys, + /// /// Removes the pathing to the global state entry of the specified key. The pruned element remains reachable from /// previously generated global state root hashes, but will not be included in the next generated global state /// root hash and subsequent state accumulated from it. /// Prune, + /// /// Represents the case where applying a transform would cause an error. /// @@ -61,35 +71,90 @@ public enum TransformKindV2 /// public class Kind { - } - + /// /// A transformation performed while executing a deploy. /// - public class TransformV2 + public class Transform { + protected int _version; + + /// + /// Returns the version of the block. + /// + public int Version + { + get { return _version; } + } + + protected TransformV1 _transformV1; + + public static explicit operator TransformV1(Transform transform) + { + if(transform._version == 1) + return transform._transformV1; + + throw new InvalidCastException("Version2 transform cannot be converted to Version1"); + } + + public static explicit operator Transform(TransformV1 transform) + { + TransformKind kind = transform.Kind switch + { + TransformKindV1.Identity => TransformKind.Identity, + TransformKindV1.WriteAccount => TransformKind.Write, + TransformKindV1.WriteAddressableEntity => TransformKind.Write, + TransformKindV1.WriteBid => TransformKind.Write, + TransformKindV1.WriteBidKind => TransformKind.Write, + TransformKindV1.WriteCLValue => TransformKind.Write, + TransformKindV1.WriteContract => TransformKind.Write, + TransformKindV1.WriteContractPackage => TransformKind.Write, + TransformKindV1.WriteContractWasm => TransformKind.Write, + TransformKindV1.WriteDeployInfo => TransformKind.Write, + TransformKindV1.WriteEraInfo => TransformKind.Write, + TransformKindV1.WriteTransfer => TransformKind.Write, + TransformKindV1.WriteUnbonding => TransformKind.Write, + TransformKindV1.WriteWithdraw => TransformKind.Write, + TransformKindV1.AddInt32 => TransformKind.AddInt32, + TransformKindV1.AddUInt64 => TransformKind.AddUInt64, + TransformKindV1.AddUInt128 => TransformKind.AddUInt128, + TransformKindV1.AddUInt256 => TransformKind.AddUInt256, + TransformKindV1.AddUInt512 => TransformKind.AddUInt512, + TransformKindV1.AddKeys => TransformKind.AddKeys, + TransformKindV1.Failure => TransformKind.Failure, + TransformKindV1.Prune => TransformKind.Prune, + }; + + return new Transform + { + Key = transform.Key, + TransformKind = kind, + Value = transform.Value, + }; + } + /// /// The formatted string of the `Key`. /// [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public GlobalStateKey Key { get; init; } - + /// /// Representation of a single transformation occurring during execution.\n\nNote that all arithmetic - /// variants of `TransformKindV2` are commutative which means that a given collection of them can be + /// variants of `TransformKind` are commutative which means that a given collection of them can be /// executed in any order to produce the same end result. /// - public TransformKindV2 TransformKind { get; init; } - + public TransformKind TransformKind { get; init; } + /// /// Data associated to some type of transforms /// public object Value { get; init; } - - public class TransformV2Converter : JsonConverter + + public class TransformConverter : JsonConverter { - public override TransformV2 Read( + public override Transform Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) @@ -100,7 +165,7 @@ public override TransformV2 Read( reader.Read(); // start object string key = null; - TransformKindV2? kind = null; + TransformKind? kind = null; object value = null; while (reader.TokenType == JsonTokenType.PropertyName) @@ -118,7 +183,7 @@ public override TransformV2 Read( { var stype = reader.GetString(); if (stype != null) - kind = EnumCompat.Parse(stype); + kind = EnumCompat.Parse(stype); reader.Read(); } else if (reader.TokenType == JsonTokenType.StartObject) @@ -126,43 +191,43 @@ public override TransformV2 Read( reader.Read(); var stype = reader.GetString(); if (stype != null) - kind = EnumCompat.Parse(stype); + kind = EnumCompat.Parse(stype); reader.Read(); switch (kind) { - case TransformKindV2.Write: + case TransformKind.Write: value = JsonSerializer.Deserialize(ref reader, options); reader.Read(); // end object break; - case TransformKindV2.AddInt32: + case TransformKind.AddInt32: value = reader.GetInt32(); reader.Read(); break; - case TransformKindV2.AddUInt64: + case TransformKind.AddUInt64: value = reader.GetUInt64(); reader.Read(); break; - case TransformKindV2.AddUInt128: + case TransformKind.AddUInt128: value = BigInteger.Parse(reader.GetString() ?? "0"); reader.Read(); break; - case TransformKindV2.AddUInt256: + case TransformKind.AddUInt256: value = BigInteger.Parse(reader.GetString() ?? "0"); reader.Read(); break; - case TransformKindV2.AddUInt512: + case TransformKind.AddUInt512: value = BigInteger.Parse(reader.GetString() ?? "0"); reader.Read(); break; - case TransformKindV2.AddKeys: + case TransformKind.AddKeys: value = JsonSerializer.Deserialize>(ref reader, options); reader.Read(); // end array break; - case TransformKindV2.Prune: + case TransformKind.Prune: value = GlobalStateKey.FromString(reader.GetString()); reader.Read(); break; - case TransformKindV2.Failure: + case TransformKind.Failure: value = reader.GetString(); reader.Read(); break; @@ -175,10 +240,10 @@ public override TransformV2 Read( if (key != null & kind != null) { - return new TransformV2() + return new Transform() { Key = GlobalStateKey.FromString(key), - TransformKind = kind ?? TransformKindV2.Identity, + TransformKind = kind ?? TransformKind.Identity, Value = value }; } @@ -188,7 +253,7 @@ public override TransformV2 Read( public override void Write( Utf8JsonWriter writer, - TransformV2 value, + Transform value, JsonSerializerOptions options) { throw new NotImplementedException("Write method for TransformV2 not yet implemented"); diff --git a/Casper.Network.SDK/Types/TransformV1.cs b/Casper.Network.SDK/Types/TransformV1.cs index 83eb94f..7f638b5 100644 --- a/Casper.Network.SDK/Types/TransformV1.cs +++ b/Casper.Network.SDK/Types/TransformV1.cs @@ -49,7 +49,7 @@ public class TransformV1 /// /// The type of transform /// - public TransformKindV1 Type { get; init; } + public TransformKindV1 Kind { get; init; } /// /// Data associated to some type of transforms @@ -179,7 +179,7 @@ public override TransformV1 Read( return new TransformV1() { Key = GlobalStateKey.FromString(key), - Type = type ?? TransformKindV1.Identity, + Kind = type ?? TransformKindV1.Identity, Value = value }; } From 52050871a8a2ad4b992dfdffbf0c544353554c51 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 5 Jun 2024 12:16:32 +0200 Subject: [PATCH 021/126] fix Transfer cast Signed-off-by: David Hernando --- Casper.Network.SDK/Types/ExecutionResult.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Casper.Network.SDK/Types/ExecutionResult.cs b/Casper.Network.SDK/Types/ExecutionResult.cs index 237a17c..0324b14 100644 --- a/Casper.Network.SDK/Types/ExecutionResult.cs +++ b/Casper.Network.SDK/Types/ExecutionResult.cs @@ -35,8 +35,12 @@ public static explicit operator ExecutionResult(ExecutionResultV1 executionResul var v2Transfers = executionResult.Transfers.Select(key => { var transform = executionResult.Effect.Transforms.FirstOrDefault(tr => tr.Key.Equals(key)); - if (transform != null && transform.Kind == TransformKindV1.WriteTransfer) - return (Transfer)transform.Value; + if (transform != null && + transform.Kind == TransformKindV1.WriteTransfer && + transform.Value is TransferV1 transferV1) + { + return (Transfer)transferV1; + } return null; }).ToList(); From cd02a4eed535b30e703f0ab23c8ae3ef9e529c67 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 5 Jun 2024 12:34:18 +0200 Subject: [PATCH 022/126] CSDK-112 align query_balance_details with RC2 schema. Signed-off-by: David Hernando --- Casper.Network.SDK/JsonRpc/CasperMethods.cs | 35 ++------------------- Casper.Network.SDK/NetCasperClient.cs | 23 +++++++------- Casper.Network.SDK/Types/StateIdentifier.cs | 4 +-- 3 files changed, 16 insertions(+), 46 deletions(-) diff --git a/Casper.Network.SDK/JsonRpc/CasperMethods.cs b/Casper.Network.SDK/JsonRpc/CasperMethods.cs index 0c31f38..66264a7 100644 --- a/Casper.Network.SDK/JsonRpc/CasperMethods.cs +++ b/Casper.Network.SDK/JsonRpc/CasperMethods.cs @@ -233,43 +233,14 @@ public class QueryBalanceDetails : RpcMethod /// /// The identifier to obtain the purse corresponding to balance query. /// The identifier for the state used for the query, if none is passed, the latest block will be used. - public QueryBalanceDetails(IPurseIdentifier purseIdentifier, IBlockIdentifier blockIdentifier) : base("query_balance_details") + public QueryBalanceDetails(IPurseIdentifier purseIdentifier, StateIdentifier stateIdentifier = null) : base("query_balance_details") { this.Parameters = new Dictionary { {"purse_identifier", purseIdentifier.GetPurseIdentifier()}, }; - if(blockIdentifier != null) - this.Parameters.Add("state_identifier", new Dictionary - { - { "block", blockIdentifier.GetBlockIdentifier()} - }); - } - - /// - /// Query for full balance information using a purse identifier and a state identifier - /// - /// The identifier to obtain the purse corresponding to balance query. - /// The state root hash used for the query. - /// Timestamp for holds lookup. - public QueryBalanceDetails(IPurseIdentifier purseIdentifier, string stateRootHash, string timestamp) : base("query_balance_details") - { - this.Parameters = new Dictionary - { - {"purse_identifier", purseIdentifier.GetPurseIdentifier()}, - {"state_identifier", - new Dictionary - { - { - "state_root", new Dictionary - { - {"state_root_hash", stateRootHash}, - {"timestamp", timestamp}, - } - }, - } - }, - }; + if(stateIdentifier != null) + this.Parameters.Add("state_identifier", stateIdentifier.GetParam()); } } diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index 5a66265..8bfa60d 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -201,7 +201,7 @@ public async Task> QueryState(string keyHash, ListThe global state key formatted as a string to query the value from the network. /// Height of the block to check the stored value in. /// The path components starting from the key as base (use '/' as separator). - public async Task> QueryGlobalState(string key, int height, + public async Task> QueryGlobalState(string key, ulong height, string path = null) { var method = new QueryGlobalState(key, StateIdentifier.WithBlockHeight(height), path?.Split(new char[] {'/'})); @@ -359,7 +359,7 @@ public async Task> GetAccountBalanceWithBlockHash( /// Purse URef key. /// Height of the block. public async Task> GetAccountBalance(URef purseURef, - int blockHeight) + ulong blockHeight) { var method = new GetBalance(purseURef, StateIdentifier.WithBlockHeight(blockHeight)); return await SendRpcRequestAsync(method); @@ -371,7 +371,7 @@ public async Task> GetAccountBalance(URef purseURe /// The account hash of the account to request the balance. /// Height of the block. public async Task> GetAccountBalance(AccountHashKey accountHash, - int blockHeight) + ulong blockHeight) { var method = new GetBalance(accountHash, StateIdentifier.WithBlockHeight(blockHeight)); return await SendRpcRequestAsync(method); @@ -383,7 +383,7 @@ public async Task> GetAccountBalance(AccountHashKe /// The public key of the account to request the balance. /// Height of the block. public async Task> GetAccountBalance(PublicKey publicKey, - int blockHeight) + ulong blockHeight) { var method = new GetBalance(publicKey, StateIdentifier.WithBlockHeight(blockHeight)); return await SendRpcRequestAsync(method); @@ -397,7 +397,7 @@ public async Task> GetAccountBalance(PublicKey pub public async Task> QueryBalanceDetails(IPurseIdentifier purseIdentifier, string blockHash = null) { - var method = new QueryBalanceDetails(purseIdentifier, blockHash != null ? new BlockIdentifier(blockHash) : null); + var method = new QueryBalanceDetails(purseIdentifier, blockHash != null ? StateIdentifier.WithBlockHash(blockHash) : null); return await SendRpcRequestAsync(method); } @@ -407,9 +407,9 @@ public async Task> QueryBalanceDetails(IP /// A PublicKey, AccountHashKey, URef or EntityAddr to identify a purse. /// Height of the block. public async Task> QueryBalanceDetails(IPurseIdentifier purseIdentifier, - UInt64 blockHeight) + ulong blockHeight) { - var method = new QueryBalanceDetails(purseIdentifier, new BlockIdentifier(blockHeight)); + var method = new QueryBalanceDetails(purseIdentifier, StateIdentifier.WithBlockHeight(blockHeight)); return await SendRpcRequestAsync(method); } @@ -417,12 +417,11 @@ public async Task> QueryBalanceDetails(IP /// Queries the balance information including total, available, and holds. /// /// A PublicKey, AccountHashKey, URef or EntityAddr to identify a purse. - /// The state root hash used for the query. - /// Timestamp for holds lookup. - public async Task> QueryBalanceDetails(IPurseIdentifier purseIdentifier, - string stateRootHash, string timestamp) + /// the state root hash. + public async Task> QueryBalanceDetailsWithStateRootHash(IPurseIdentifier purseIdentifier, + string stateRootHash) { - var method = new QueryBalanceDetails(purseIdentifier, stateRootHash, timestamp); + var method = new QueryBalanceDetails(purseIdentifier, StateIdentifier.WithStateRootHash(stateRootHash)); return await SendRpcRequestAsync(method); } diff --git a/Casper.Network.SDK/Types/StateIdentifier.cs b/Casper.Network.SDK/Types/StateIdentifier.cs index c951a80..14f3eaa 100644 --- a/Casper.Network.SDK/Types/StateIdentifier.cs +++ b/Casper.Network.SDK/Types/StateIdentifier.cs @@ -6,7 +6,7 @@ public class StateIdentifier { private string _stateRootHash; private string _blockHash; - private int? _blockHeight; + private ulong? _blockHeight; private StateIdentifier() { @@ -32,7 +32,7 @@ public static StateIdentifier WithBlockHash(string blockHash) }; } - public static StateIdentifier WithBlockHeight(int blockHeight) + public static StateIdentifier WithBlockHeight(ulong blockHeight) { return new StateIdentifier { From 56e0e0a0f8a0d94745f88266f1c527c30809f7d6 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 11 Jun 2024 11:33:28 +0200 Subject: [PATCH 023/126] CSDK-179 added backwards compatibility to FinalitySignature parsing. Signed-off-by: David Hernando --- Casper.Network.SDK.Test/SSETypesTest.cs | 52 ++++-- .../TestData/finality_signature_v156.json | 8 + Casper.Network.SDK/SSE/FinalitySignature.cs | 163 +++++++++++++----- 3 files changed, 166 insertions(+), 57 deletions(-) create mode 100644 Casper.Network.SDK.Test/TestData/finality_signature_v156.json diff --git a/Casper.Network.SDK.Test/SSETypesTest.cs b/Casper.Network.SDK.Test/SSETypesTest.cs index 7e7b630..8457a5f 100644 --- a/Casper.Network.SDK.Test/SSETypesTest.cs +++ b/Casper.Network.SDK.Test/SSETypesTest.cs @@ -7,6 +7,40 @@ namespace NetCasperTest { public class SSETypesTest { + [Test] + public void FinalitySignatureV1Test() + { + string testFile = TestContext.CurrentContext.TestDirectory + "/TestData/finality_signature_v156.json"; + var json = File.ReadAllText(testFile); + + var doc = System.Text.Json.JsonDocument.Parse(json); + + json = doc.RootElement.GetProperty("FinalitySignature").GetRawText(); + // Assert.AreEqual(3, doc.RootElement.EnumerateObject().Count()); + // Assert.AreEqual("U512", ); + var value = JsonSerializer.Deserialize(json); + Assert.IsNotNull(value); + + Assert.AreEqual(1, value.Version); + Assert.AreEqual("d800de72aa40c6df064c714d3a6d8b6fab73f68742d8468b84efd6616bbb10bb", value.BlockHash); + Assert.AreEqual(0, value.BlockHeight); + Assert.AreEqual(13859, value.EraId); + Assert.IsNull(value.ChainNameHash); + Assert.AreEqual("01221b61b83b889898363501c7defd7baa6729989d8fce2dfffea4632017fd46cc34b88bc85fa570a6e9ada829c67b9fdaa78e8dee2f07d346bcad0010e1d3df0f", + value.Signature.ToHexString().ToLowerInvariant()); + Assert.AreEqual("01b71b2d746681b4e0f44ef137d72ee0d42122b08ef569dc65bb0395cb624f99e5", + value.PublicKey.ToString().ToLowerInvariant()); + + var v1 = (FinalitySignatureV1)value; + + Assert.AreEqual("d800de72aa40c6df064c714d3a6d8b6fab73f68742d8468b84efd6616bbb10bb", v1.BlockHash); + Assert.AreEqual(13859, v1.EraId); + Assert.AreEqual("01221b61b83b889898363501c7defd7baa6729989d8fce2dfffea4632017fd46cc34b88bc85fa570a6e9ada829c67b9fdaa78e8dee2f07d346bcad0010e1d3df0f", + v1.Signature.ToHexString().ToLowerInvariant()); + Assert.AreEqual("01b71b2d746681b4e0f44ef137d72ee0d42122b08ef569dc65bb0395cb624f99e5", + v1.PublicKey.ToString().ToLowerInvariant()); + } + [Test] public void FinalitySignatureV2Test() { @@ -18,20 +52,18 @@ public void FinalitySignatureV2Test() json = doc.RootElement.GetProperty("FinalitySignature").GetRawText(); // Assert.AreEqual(3, doc.RootElement.EnumerateObject().Count()); // Assert.AreEqual("U512", ); - var value = JsonSerializer.Deserialize(json); + var value = JsonSerializer.Deserialize(json); Assert.IsNotNull(value); - var v2 = value.FinalitySignatureV2; - Assert.IsNotNull(v2); - Assert.AreEqual("13a5603c1dad7d4e4d2ce81313c35043172e1535363c3ec428f559dff5704ea5", v2.BlockHash); - Assert.AreEqual(54, v2.BlockHeight); - Assert.AreEqual(5, v2.EraId); - Assert.AreEqual("8a09603fc862b15412b60a050d71f69c57601b6da7382dd56b9a3f300822bb75", v2.ChainNameHash); + Assert.AreEqual(2, value.Version); + Assert.AreEqual("13a5603c1dad7d4e4d2ce81313c35043172e1535363c3ec428f559dff5704ea5", value.BlockHash); + Assert.AreEqual(54, value.BlockHeight); + Assert.AreEqual(5, value.EraId); + Assert.AreEqual("8a09603fc862b15412b60a050d71f69c57601b6da7382dd56b9a3f300822bb75", value.ChainNameHash); Assert.AreEqual("01a12e3601b4d5c82177231910adaabf50d8a52416e756efee1694ee7534ae16fdb59a7370d564715615736074850b9255ee246c8ffa2502c167c6ef8a86b06504", - v2.Signature.ToHexString().ToLowerInvariant()); + value.Signature.ToHexString().ToLowerInvariant()); Assert.AreEqual("01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", - v2.PublicKey.ToString().ToLowerInvariant()); - + value.PublicKey.ToString().ToLowerInvariant()); } } } \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/finality_signature_v156.json b/Casper.Network.SDK.Test/TestData/finality_signature_v156.json new file mode 100644 index 0000000..25a0d75 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/finality_signature_v156.json @@ -0,0 +1,8 @@ +{ + "FinalitySignature": { + "block_hash": "d800de72aa40c6df064c714d3a6d8b6fab73f68742d8468b84efd6616bbb10bb", + "era_id": 13859, + "signature": "01221b61b83b889898363501c7defd7baa6729989d8fce2dfffea4632017fd46cc34b88bc85fa570a6e9ada829c67b9fdaa78e8dee2f07d346bcad0010e1d3df0f", + "public_key": "01b71b2d746681b4e0f44ef137d72ee0d42122b08ef569dc65bb0395cb624f99e5" + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/SSE/FinalitySignature.cs b/Casper.Network.SDK/SSE/FinalitySignature.cs index b7ac4e2..1d944c5 100644 --- a/Casper.Network.SDK/SSE/FinalitySignature.cs +++ b/Casper.Network.SDK/SSE/FinalitySignature.cs @@ -8,7 +8,7 @@ namespace Casper.Network.SDK.SSE /// /// A validator's signature of a block, confirming it is finalized. Produced in Casper v1.x /// - public class FinalitySignatureV1 : FinalitySignature + public class FinalitySignatureV1 { /// /// The block hash @@ -37,10 +37,8 @@ public class FinalitySignatureV1 : FinalitySignature public Signature Signature { get; init; } } - /// - /// A validator's signature of a block, confirming it is finalized. Produced in Casper v2.x - /// - public class FinalitySignatureV2 : FinalitySignatureV1 + + internal class FinalitySignatureV2 : FinalitySignatureV1 { /// /// The block height @@ -55,81 +53,152 @@ public class FinalitySignatureV2 : FinalitySignatureV1 public string ChainNameHash { get; init; } } - [JsonConverter(typeof(FinalitySignature.FinalitySignatureConverter))] - public interface IFinalitySignature + internal class FinalitySignatureCompat { - public int Version { get; } + [JsonPropertyName("V1")] + public FinalitySignatureV1 Version1 { get; init; } - public FinalitySignatureV1 FinalitySignatureV1 { get; } + [JsonPropertyName("V2")] + public FinalitySignatureV2 Version2 { get; init; } - public FinalitySignatureV2 FinalitySignatureV2 { get; } + [JsonPropertyName("block_hash")] + public string BlockHash { get; init; } + + [JsonPropertyName("era_id")] + public ulong EraId { get; init; } + + [JsonPropertyName("public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey PublicKey { get; init; } + + [JsonPropertyName("signature")] + [JsonConverter(typeof(Signature.SignatureConverter))] + public Signature Signature { get; init; } } /// /// A validator's signature of a block, confirming it is finalized. /// - public class FinalitySignature : IFinalitySignature + [JsonConverter(typeof(FinalitySignatureConverter))] + public class FinalitySignature { protected int _version; /// - /// Returns the version of the transfer. + /// Returns the version of the finality signature. /// public int Version { get { return _version; } } + protected FinalitySignatureV1 _finalitySignatureV1; + + public static explicit operator FinalitySignatureV1(FinalitySignature finalitySignature) + { + if(finalitySignature._version == 1) + return finalitySignature._finalitySignatureV1; + + throw new InvalidCastException("Version2 FinalitySignature cannot be converted to Version1"); + } + + public static explicit operator FinalitySignature(FinalitySignatureV1 finalitySignature) + { + return new FinalitySignature + { + _version = 1, + _finalitySignatureV1 = finalitySignature, + BlockHash = finalitySignature.BlockHash, + BlockHeight = 0, + EraId = finalitySignature.EraId, + PublicKey= finalitySignature.PublicKey, + Signature = finalitySignature.Signature, + ChainNameHash = null, + }; + } + /// - /// Returns the transfer as a Version1 transfer object. + /// The block hash + /// + [JsonPropertyName("block_hash")] + public string BlockHash { get; init; } + + /// + /// The block height /// - FinalitySignatureV1 IFinalitySignature.FinalitySignatureV1 => this as FinalitySignatureV1; + [JsonPropertyName("block_height")] + public ulong BlockHeight { get; init; } + + /// + /// The hash of the chain name of the associated block. + /// + [JsonPropertyName("chain_name_hash")] + public string ChainNameHash { get; init; } /// - /// Returns the transfer as a Version2 transfer object. + /// The block era id. /// - FinalitySignatureV2 IFinalitySignature.FinalitySignatureV2 => this as FinalitySignatureV2; + [JsonPropertyName("era_id")] + public ulong EraId { get; init; } /// - /// Json converter class to serialize/deserialize a Block to/from Json + /// Validator public key /// - public class FinalitySignatureConverter : JsonConverter - { - public override bool CanConvert(Type typeToConvert) - { - return typeToConvert == typeof(IFinalitySignature) || - typeToConvert == typeof(FinalitySignature); - } + [JsonPropertyName("public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey PublicKey { get; init; } - public override IFinalitySignature Read( + /// + /// Validator signature + /// + [JsonPropertyName("signature")] + [JsonConverter(typeof(Signature.SignatureConverter))] + public Signature Signature { get; init; } + + /// + /// Json converter class to serialize/deserialize a FinalitySignature to/from Json + /// + public class FinalitySignatureConverter : JsonConverter + { + public override FinalitySignature Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { try { - reader.Read(); - var version = reader.GetString(); - reader.Read(); - switch (version) + var fsCompat = JsonSerializer.Deserialize(ref reader, options); + if (fsCompat.BlockHash != null) + { + var v1 = new FinalitySignatureV1() + { + BlockHash = fsCompat.BlockHash, + EraId = fsCompat.EraId, + PublicKey = fsCompat.PublicKey, + Signature = fsCompat.Signature, + + }; + return (FinalitySignature)v1; + } + if (fsCompat.Version1 != null) + { + return (FinalitySignature)fsCompat.Version1; + } + if (fsCompat.Version2 != null) { - case "V1": + return new FinalitySignature { - var finalitySignature1 = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - finalitySignature1._version = 1; - return finalitySignature1; - } - case "V2": - var finalitySignature2 = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - finalitySignature2._version = 2; - return finalitySignature2; - default: - throw new JsonException("Expected V1 or V2"); + _version = 2, + BlockHash = fsCompat.Version2.BlockHash, + BlockHeight = fsCompat.Version2.BlockHeight, + EraId = fsCompat.Version2.EraId, + PublicKey = fsCompat.Version2.PublicKey, + Signature = fsCompat.Version2.Signature, + ChainNameHash = fsCompat.Version2.ChainNameHash, + }; } - ; + throw new JsonException("Cannot deserialize FinalitySignature"); } catch (Exception e) { @@ -139,7 +208,7 @@ public override IFinalitySignature Read( public override void Write( Utf8JsonWriter writer, - IFinalitySignature finalitySignature, + FinalitySignature finalitySignature, JsonSerializerOptions options) { switch (finalitySignature.Version) @@ -147,13 +216,13 @@ public override void Write( case 1: writer.WritePropertyName("V1"); writer.WriteStartObject(); - JsonSerializer.Serialize(finalitySignature as FinalitySignatureV1, options); + JsonSerializer.Serialize((FinalitySignatureV1)finalitySignature, options); writer.WriteEndObject(); break; case 2: writer.WritePropertyName("V2"); writer.WriteStartObject(); - JsonSerializer.Serialize(finalitySignature as FinalitySignatureV2, options); + JsonSerializer.Serialize(finalitySignature, options); writer.WriteEndObject(); break; default: @@ -162,4 +231,4 @@ public override void Write( } } } -} \ No newline at end of file +} From 4a7aa48a311bfdc399b19e9f464300b9f5e6ab46 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 11 Jun 2024 18:13:08 +0200 Subject: [PATCH 024/126] CSDK-123 use PurseIdentifier in CasperMethods to allow entity adresses. Signed-off-by: David Hernando --- Casper.Network.SDK/JsonRpc/CasperMethods.cs | 44 +++++---------------- 1 file changed, 10 insertions(+), 34 deletions(-) diff --git a/Casper.Network.SDK/JsonRpc/CasperMethods.cs b/Casper.Network.SDK/JsonRpc/CasperMethods.cs index 66264a7..1421d06 100644 --- a/Casper.Network.SDK/JsonRpc/CasperMethods.cs +++ b/Casper.Network.SDK/JsonRpc/CasperMethods.cs @@ -186,43 +186,19 @@ public GetBalance(string purseURef, string stateRootHash) : base("state_get_bala }; } - public GetBalance(URef uref, StateIdentifier stateIdentifier) : base("query_balance") - { - Dictionary mainPurse = new Dictionary - { - {"purse_uref", uref.ToString()} - }; - this.Parameters = new Dictionary - { - {"purse_identifier", mainPurse} - }; - this.Parameters.Add("state_identifier", stateIdentifier.GetParam()); - } - - public GetBalance(AccountHashKey key, StateIdentifier stateIdentifier) : base("query_balance") - { - Dictionary mainPurse = new Dictionary - { - {"main_purse_under_account_hash", key.ToString()} - }; - this.Parameters = new Dictionary - { - {"purse_identifier", mainPurse} - }; - this.Parameters.Add("state_identifier", stateIdentifier.GetParam()); - } - - public GetBalance(PublicKey key, StateIdentifier stateIdentifier) : base("query_balance") + /// + /// Query for balance information using a purse identifier and a state identifier + /// + /// The identifier to obtain the purse corresponding to balance query. + /// The identifier for the state used for the query, if none is passed, the latest block will be used. + public GetBalance(IPurseIdentifier purseIdentifier, StateIdentifier stateIdentifier = null) : base("query_balance") { - Dictionary mainPurse = new Dictionary - { - {"main_purse_under_public_key", key.ToString()} - }; this.Parameters = new Dictionary { - {"purse_identifier", mainPurse} + {"purse_identifier", purseIdentifier.GetPurseIdentifier()}, }; - this.Parameters.Add("state_identifier", stateIdentifier.GetParam()); + if(stateIdentifier != null) + this.Parameters.Add("state_identifier", stateIdentifier.GetParam()); } } @@ -232,7 +208,7 @@ public class QueryBalanceDetails : RpcMethod /// Query for full balance information using a purse identifier and a state identifier /// /// The identifier to obtain the purse corresponding to balance query. - /// The identifier for the state used for the query, if none is passed, the latest block will be used. + /// The identifier for the state used for the query, if none is passed, the latest block will be used. public QueryBalanceDetails(IPurseIdentifier purseIdentifier, StateIdentifier stateIdentifier = null) : base("query_balance_details") { this.Parameters = new Dictionary From 9cf2350d3dce03038c3bd7ad46c30dcda0f40a7b Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 14 May 2024 17:37:24 +0200 Subject: [PATCH 025/126] CSDK-114 chain_get_block CSDK-155 chain_get_block_transfers Signed-off-by: David Hernando --- .../JsonRpc/ResultTypes/GetBlockResult.cs | 4 +- .../ResultTypes/GetBlockTransfersResult.cs | 4 +- Casper.Network.SDK/SSE/BlockAdded.cs | 7 +- Casper.Network.SDK/Types/Block.cs | 360 +++++++++++++++--- Casper.Network.SDK/Types/EraEnd.cs | 61 ++- Casper.Network.SDK/Types/InitiatorAddr.cs | 56 +++ Casper.Network.SDK/Types/TransactionHash.cs | 14 + Casper.Network.SDK/Types/Transfer.cs | 165 +++++++- 8 files changed, 607 insertions(+), 64 deletions(-) create mode 100644 Casper.Network.SDK/Types/InitiatorAddr.cs create mode 100644 Casper.Network.SDK/Types/TransactionHash.cs diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs index 1547c4f..2874985 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs @@ -11,7 +11,7 @@ public class GetBlockResult : RpcResult /// /// The block, if found. /// - [JsonPropertyName("block")] - public Block Block { get; init; } + [JsonPropertyName("block_with_signatures")] + public BlockWithSignatures BlockWithSignatures { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockTransfersResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockTransfersResult.cs index b3e6399..05231b3 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockTransfersResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockTransfersResult.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; using Casper.Network.SDK.Types; namespace Casper.Network.SDK.JsonRpc.ResultTypes @@ -19,6 +20,7 @@ public class GetBlockTransfersResult : RpcResult /// The block's transfers /// [JsonPropertyName("transfers")] - public List Transfers { get; init; } + [JsonConverter(typeof(GenericListConverter))] + public List Transfers { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/SSE/BlockAdded.cs b/Casper.Network.SDK/SSE/BlockAdded.cs index ae96e0e..a0df706 100644 --- a/Casper.Network.SDK/SSE/BlockAdded.cs +++ b/Casper.Network.SDK/SSE/BlockAdded.cs @@ -11,11 +11,14 @@ public class BlockAdded /// /// The Block hash. /// - [JsonPropertyName("block_hash")] public string BlockHash { get; init; } + [JsonPropertyName("block_hash")] + public string BlockHash { get; init; } /// /// The Block data. /// - [JsonPropertyName("block")] public Block Block { get; init; } + [JsonPropertyName("block")] + [JsonConverter(typeof(Block.BlockConverter))] + public IBlock Block { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Block.cs b/Casper.Network.SDK/Types/Block.cs index 0233bfe..ba53a79 100644 --- a/Casper.Network.SDK/Types/Block.cs +++ b/Casper.Network.SDK/Types/Block.cs @@ -3,75 +3,153 @@ using System.ComponentModel; using System.Text.Json; using System.Text.Json.Serialization; +using Org.BouncyCastle.Asn1.X509.Qualified; namespace Casper.Network.SDK.Types { + public abstract class BlockHeader + { + /// + /// Json converter class to serialize/deserialize a Block to/from Json + /// + public class BlockHeaderConverter : JsonConverter + { + public override BlockHeader Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + reader.Read(); + var version = reader.GetString(); + reader.Read(); + switch (version.ToLowerInvariant()) + { + case "version1": + { + var block1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return block1; + } + case "version2": + var block2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return block2; + default: + throw new JsonException("Expected Version1 or Version2"); + } + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + BlockHeader blockHeader, + JsonSerializerOptions options) + { + if (blockHeader is BlockHeaderV2) + { + writer.WritePropertyName("Version2"); + writer.WriteStartObject(); + JsonSerializer.Serialize(blockHeader as BlockHeaderV2, options); + writer.WriteEndObject(); + } + else + { + writer.WritePropertyName("Version1"); + writer.WriteStartObject(); + JsonSerializer.Serialize(blockHeader as BlockHeaderV1, options); + writer.WriteEndObject(); + } + } + } + } + /// /// A block header /// - public class BlockHeader + public class BlockHeaderV1 : BlockHeader { /// - /// Accumulated seed. + /// A seed needed for initializing a future era. /// [JsonPropertyName("accumulated_seed")] public string AccumulatedSeed { get; init; } /// - /// The body hash. + /// The hash of the block's body. /// [JsonPropertyName("body_hash")] public string BodyHash { get; init; } /// - /// The era end. + /// The `EraEnd` of a block if it is a switch block. /// [JsonPropertyName("era_end")] - public EraEnd EraEnd { get; init; } + public EraEndV1 EraEnd { get; init; } /// - /// The block era id. + /// The era ID in which this block was created. /// [JsonPropertyName("era_id")] public ulong EraId { get; init; } /// - /// The block height. + /// The height of this block, i.e. the number of ancestors. /// [JsonPropertyName("height")] public ulong Height { get; init; } - + /// - /// The parent hash. + /// The parent block's hash. /// [JsonPropertyName("parent_hash")] public string ParentHash { get; init; } - + /// - /// The protocol version. + /// The protocol version of the network from when this block was created. /// - [JsonPropertyName("protocol_version")] + [JsonPropertyName("protocol_version")] public string ProtocolVersion { get; init; } - + /// - /// Randomness bit. + /// A random bit needed for initializing a future era. /// [JsonPropertyName("random_bit")] public bool RandomBit { get; init; } - + /// - /// The state root hash. + /// The root hash of global state after the deploys in this block have been executed. /// [JsonPropertyName("state_root_hash")] public string StateRootHash { get; init; } - + /// - /// The block timestamp. + /// The timestamp from when the block was proposed. /// - [JsonPropertyName("timestamp")] + [JsonPropertyName("timestamp")] public string Timestamp { get; init; } } + public class BlockHeaderV2 : BlockHeaderV1 + { + /// + /// The `EraEnd` of a block if it is a switch block. + /// + [JsonPropertyName("era_end")] + public EraEndV2 EraEnd { get; init; } + + /// + /// The gas price of the era. + /// + [JsonPropertyName("current_gas_price")] + public UInt16 CurrentGasPrice { get; init; } + } + /// /// Validator that proposed the block /// @@ -89,12 +167,12 @@ public bool isSystem get => this.IsSystem; set => this.IsSystem = value; } - + /// /// Validator's public key /// public PublicKey PublicKey { get; set; } - + /// /// Json converter class to serialize/deserialize a Proposer to/from Json /// @@ -134,62 +212,113 @@ public override void Write( } } } - + /// /// A block body /// - public class BlockBody + public class BlockBodyV1 { + public virtual uint Version + { + get { return 1; } + } + /// - /// List of Deploy hashes included in the block + /// The deploy hashes of the non-transfer deploys within the block. /// [JsonPropertyName("deploy_hashes")] public List DeployHashes { get; init; } - + /// /// Public key of the validator that proposed the block /// [JsonPropertyName("proposer")] [JsonConverter(typeof(Proposer.ProposerConverter))] public Proposer Proposer { get; init; } - + /// - /// List of Transfer hashes included in the block + /// The deploy hashes of the transfers within the block. /// [JsonPropertyName("transfer_hashes")] public List TransferHashes { get; init; } } /// - /// Block's finality signature. + /// A block body /// - public class BlockProof + public class BlockBodyV2 { /// - /// Validator public key + /// The hashes of the installer/upgrader transactions within the block. /// - [JsonPropertyName("public_key")] - [JsonConverter(typeof(PublicKey.PublicKeyConverter))] - public PublicKey PublicKey { get; init; } - + [JsonPropertyName("install_upgrade")] + public List InstallUpgrade { get; init; } + /// - /// Validator signature + /// The hashes of the auction transactions within the block. /// - [JsonPropertyName("signature")] - [JsonConverter(typeof(Signature.SignatureConverter))] - public Signature Signature { get; init; } + [JsonPropertyName("auction")] + public List Auction { get; init; } + + /// + /// The hashes of all other (non-installer/upgrader) transactions within the block. + /// + [JsonPropertyName("standard")] + public List Standard { get; init; } + + /// + /// Public key of the validator that proposed the block + /// + [JsonPropertyName("proposer")] + [JsonConverter(typeof(Proposer.ProposerConverter))] + public Proposer Proposer { get; init; } + + /// + /// The hashes of the mint transactions within the block. + /// + [JsonPropertyName("mint")] + public List Mint { get; init; } + + /// + /// Describes finality signatures that will be rewarded in a block. Consists of a vector of + /// `SingleBlockRewardedSignatures`, each of which describes signatures for a single ancestor block. + /// The first entry represents the signatures for the parent block, the second for the parent of the parent, + /// and so on. + /// + [JsonPropertyName("rewarded_signatures")] + public List> RewardedSignatures { get; init; } + } + + public interface IBlock + { + public int Version { get; } + public BlockV1 BlockV1 { get; } + public BlockV2 BlockV2 { get; } + + public string Hash { get; init; } } - /// - /// A block in the network - /// - public class Block + public abstract class Block : IBlock { + protected int _version; + /// - /// Block body + /// Returns the version of the block. /// - [JsonPropertyName("body")] - public BlockBody Body { get; init; } + public int Version + { + get { return _version; } + } + + /// + /// Returns the block as a Version1 block object. + /// + BlockV1 IBlock.BlockV1 => this as BlockV1; + + /// + /// Returns the block as a Version2 block object. + /// + BlockV2 IBlock.BlockV2 => this as BlockV2; /// /// Block hash @@ -197,16 +326,151 @@ public class Block [JsonPropertyName("hash")] public string Hash { get; init; } + /// + /// Json converter class to serialize/deserialize a Block to/from Json + /// + public class BlockConverter : JsonConverter + { + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert == typeof(IBlock) || + typeToConvert == typeof(Block); + } + + public override IBlock Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + reader.Read(); + var version = reader.GetString(); + reader.Read(); + switch (version) + { + case "Version1": + { + var block1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + block1._version = 1; + return block1; + } + case "Version2": + var block2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + block2._version = 2; + return block2; + default: + throw new JsonException("Expected Version1 or Version2"); + } + + ; + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + IBlock block, + JsonSerializerOptions options) + { + switch (block.Version) + { + case 1: + writer.WritePropertyName("Version1"); + writer.WriteStartObject(); + JsonSerializer.Serialize(block as BlockV1, options); + writer.WriteEndObject(); + break; + case 2: + writer.WritePropertyName("Version2"); + writer.WriteStartObject(); + JsonSerializer.Serialize(block as BlockV2, options); + writer.WriteEndObject(); + break; + default: + throw new JsonException($"Unexpected block version {block.Version}"); + } + } + } + } + + /// + /// A block in the network + /// + public class BlockV1 : Block + { + /// + /// Block header + /// + [JsonPropertyName("header")] + public BlockHeaderV1 Header { get; init; } + + /// + /// Block body + /// + [JsonPropertyName("body")] + public BlockBodyV1 Body { get; init; } + } + + /// + /// A block in the network + /// + public class BlockV2 : Block + { /// /// Block header /// [JsonPropertyName("header")] - public BlockHeader Header { get; init; } + public BlockHeaderV2 Header { get; init; } + + /// + /// Block body + /// + [JsonPropertyName("body")] + public BlockBodyV2 Body { get; init; } + } + + /// + /// A validator's public key paired with a corresponding signature of a given block hash. + /// + public class BlockProof + { + /// + /// The validator's public key. + /// + [JsonPropertyName("public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey PublicKey { get; init; } + + /// + /// The validator's signature. + /// + [JsonPropertyName("signature")] + [JsonConverter(typeof(Signature.SignatureConverter))] + public Signature Signature { get; init; } + } + + /// + /// A JSON-friendly representation of a block and the signatures for that block + /// + public class BlockWithSignatures + { + /// + /// The block. + /// + [JsonPropertyName("block")] + [JsonConverter(typeof(Block.BlockConverter))] + public IBlock Block { get; init; } /// - /// List of proofs for this block. + /// The proofs of the block, i.e. a collection of validators' signatures of the block hash. /// [JsonPropertyName("proofs")] public List Proofs { get; init; } } -} +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/EraEnd.cs b/Casper.Network.SDK/Types/EraEnd.cs index 0674da2..9a1d32b 100644 --- a/Casper.Network.SDK/Types/EraEnd.cs +++ b/Casper.Network.SDK/Types/EraEnd.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; @@ -5,18 +6,18 @@ namespace Casper.Network.SDK.Types { /// - /// A validator reward + /// validator's public key paired with a measure of the value of its contribution to consensus, as a fraction of the configured maximum block reward. /// public class Reward { /// - /// Reward amount + /// The reward amount. /// [JsonPropertyName("amount")] public ulong Amount { get; init; } /// - /// Validator public key + /// The validator's public key. /// [JsonPropertyName("validator")] [JsonConverter(typeof(PublicKey.PublicKeyConverter))] @@ -24,38 +25,38 @@ public class Reward } /// - /// Equivocation and reward information to be included in the terminal block. + /// Equivocation, reward and validator inactivity information. /// public class EraReport { /// - /// List of public keys of the equivocators + /// The set of equivocators. /// [JsonPropertyName("equivocators")] [JsonConverter(typeof(GenericListConverter))] public List Equivocators { get; init; } /// - /// List of public keys of inactive validators + /// Validators that haven't produced any unit during the era. /// [JsonPropertyName("inactive_validators")] [JsonConverter(typeof(GenericListConverter))] public List InactiveValidators { get; init; } /// - /// List of validators with rewards + /// Rewards for finalization of earlier blocks. /// [JsonPropertyName("rewards")] public List Rewards { get; init; } } /// - /// Era end report and list of validator weights for next era + /// Information related to the end of an era, and validator weights for the following era. /// - public class EraEnd + public class EraEndV1 { /// - /// Era report + /// Equivocation, reward and validator inactivity information. /// [JsonPropertyName("era_report")] public EraReport EraReport { get; init; } @@ -67,4 +68,44 @@ public class EraEnd [JsonConverter(typeof(GenericListConverter))] public List NextEraValidatorWeights { get; init; } } + + + /// + /// Information related to the end of an era, and validator weights for the following era. + /// + public class EraEndV2 + { + /// + /// The set of equivocators. + /// + [JsonPropertyName("equivocators")] + [JsonConverter(typeof(GenericListConverter))] + public List Equivocators { get; init; } + + /// + /// Validators that haven't produced any unit during the era. + /// + [JsonPropertyName("inactive_validators")] + [JsonConverter(typeof(GenericListConverter))] + public List InactiveValidators { get; init; } + + /// + /// A list of validator weights for the next era + /// + [JsonPropertyName("next_era_validator_weights")] + [JsonConverter(typeof(GenericListConverter))] + public List NextEraValidatorWeights { get; init; } + + /// + /// The rewards distributed to the validators. + /// + [JsonPropertyName("rewards")] + public Dictionary Rewards { get; init; } + + /// + /// Next Era gas price + /// + [JsonPropertyName("next_era_gas_price")] + public UInt16 NextEraGasPrice { get; init; } + } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/InitiatorAddr.cs b/Casper.Network.SDK/Types/InitiatorAddr.cs new file mode 100644 index 0000000..6a1e87d --- /dev/null +++ b/Casper.Network.SDK/Types/InitiatorAddr.cs @@ -0,0 +1,56 @@ +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + /// + /// The address of the initiator of a TransactionV1 + /// + public class InitiatorAddr + { + /// + /// The public key of the initiator + /// + [JsonPropertyName("PublicKey")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey PublicKey { get; init; } + + /// + /// The account hash derived from the public key of the initiator + /// + [JsonPropertyName("AccountHash")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public AccountHashKey AccountHash { get; init; } + + public InitiatorAddr() + { + } + + public InitiatorAddr(PublicKey publicKey) + { + this.PublicKey = publicKey; + } + + public InitiatorAddr(AccountHashKey accountHash) + { + this.AccountHash = accountHash; + } + + public override string ToString() + { + return PublicKey != null + ? PublicKey.ToString() + : (AccountHash != null + ? AccountHash.ToString() + : null); + } + + public string ToHexString() + { + return PublicKey != null + ? PublicKey.ToString() + : (AccountHash != null + ? AccountHash.ToHexString() + : null); + } + } +} diff --git a/Casper.Network.SDK/Types/TransactionHash.cs b/Casper.Network.SDK/Types/TransactionHash.cs new file mode 100644 index 0000000..7b5e26b --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionHash.cs @@ -0,0 +1,14 @@ +namespace Casper.Network.SDK.Types +{ + public class TransactionHash + { + public string Deploy { get; init; } + + public string Version1 { get; init; } + + public override string ToString() + { + return Deploy ?? Version1; + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Transfer.cs b/Casper.Network.SDK/Types/Transfer.cs index ef892d8..c6cf43b 100644 --- a/Casper.Network.SDK/Types/Transfer.cs +++ b/Casper.Network.SDK/Types/Transfer.cs @@ -1,4 +1,6 @@ +using System; using System.Numerics; +using System.Text.Json; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; @@ -7,7 +9,7 @@ namespace Casper.Network.SDK.Types /// /// Represents a transfer from one purse to another /// - public class Transfer + public class TransferV1 : Transfer { /// /// Transfer amount @@ -63,4 +65,165 @@ public class Transfer [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public AccountHashKey To { get; init; } } + + /// + /// Represents a version 2 transfer from one purse to another + /// + public class TransferV2 : Transfer + { + /// + /// Transfer amount + /// + [JsonPropertyName("amount")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Amount { get; init; } + + /// + /// Transaction that created the transfer + /// + [JsonPropertyName("transaction_hash")] + public TransactionHash TransactionHash { get; init; } + + /// + /// Entity from which transfer was executed + /// + [JsonPropertyName("from")] + public InitiatorAddr From { get; init; } + + /// + /// Gas + /// + [JsonPropertyName("gas")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Gas { get; init; } + + /// + /// User-defined id + /// + [JsonPropertyName("id")] + public ulong? Id { get; init; } + + /// + /// Source purse + /// + [JsonPropertyName("source")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public URef Source { get; init; } + + /// + /// Target purse + /// + [JsonPropertyName("target")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public URef Target { get; init; } + + /// + /// Account to which funds are transferred + /// + [JsonPropertyName("to")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public AccountHashKey To { get; init; } + } + + public interface ITransfer + { + public int Version { get; } + public TransferV1 TransferV1 { get; } + public TransferV2 TransferV2 { get; } + } + + public class Transfer: ITransfer + { + protected int _version; + + /// + /// Returns the version of the transfer. + /// + public int Version + { + get { return _version; } + } + + /// + /// Returns the transfer as a Version1 transfer object. + /// + TransferV1 ITransfer.TransferV1 => this as TransferV1; + + /// + /// Returns the transfer as a Version2 transfer object. + /// + TransferV2 ITransfer.TransferV2 => this as TransferV2; + + /// + /// Json converter class to serialize/deserialize a Block to/from Json + /// + public class TransferConverter : JsonConverter + { + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert == typeof(ITransfer) || + typeToConvert == typeof(Transfer); + } + + public override ITransfer Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + reader.Read(); + var version = reader.GetString(); + reader.Read(); + switch (version) + { + case "Version1": + { + var transfer1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + transfer1._version = 1; + return transfer1; + } + case "Version2": + var transfer2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + transfer2._version = 2; + return transfer2; + default: + throw new JsonException("Expected Version1 or Version2"); + } + + ; + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + ITransfer transfer, + JsonSerializerOptions options) + { + switch (transfer.Version) + { + case 1: + writer.WritePropertyName("Version1"); + writer.WriteStartObject(); + JsonSerializer.Serialize(transfer as TransferV1, options); + writer.WriteEndObject(); + break; + case 2: + writer.WritePropertyName("Version2"); + writer.WriteStartObject(); + JsonSerializer.Serialize(transfer as TransferV2, options); + writer.WriteEndObject(); + break; + default: + throw new JsonException($"Unexpected transfer version {transfer.Version}"); + } + } + } + } } \ No newline at end of file From c9424c64e7b5d4475f50b795489e1e4bce036d43 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 31 May 2024 12:13:26 +0200 Subject: [PATCH 026/126] Parse blocks from Casper 1.x node (backward compat) Signed-off-by: David Hernando --- .../JsonRpc/ResultTypes/GetBlockResult.cs | 115 +++++++++++++++++- Casper.Network.SDK/Types/Block.cs | 47 +++---- 2 files changed, 129 insertions(+), 33 deletions(-) diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs index 2874985..ed4f29c 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs @@ -1,17 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; using System.Text.Json.Serialization; using Casper.Network.SDK.Types; namespace Casper.Network.SDK.JsonRpc.ResultTypes { + internal class BlockV1Compat + { + [JsonPropertyName("hash")] + public string Hash { get; init; } + + [JsonPropertyName("header")] + public BlockHeaderV1 Header { get; init; } + + [JsonPropertyName("body")] + public BlockBodyV1 Body { get; init; } + + [JsonPropertyName("proofs")] + public List Proofs { get; init; } + } + /// - /// Result for "chain_get_block" RPC response. + /// A JSON-friendly representation of a block and the signatures for that block /// - public class GetBlockResult : RpcResult + internal class BlockWithSignatures + { + /// + /// The block. + /// + [JsonPropertyName("block")] + [JsonConverter(typeof(Block.BlockConverter))] + public IBlock Block { get; init; } + + /// + /// The proofs of the block, i.e. a collection of validators' signatures of the block hash. + /// + [JsonPropertyName("proofs")] + public List Proofs { get; init; } + } + + internal class GetBlockResultCompat : RpcResult { /// /// The block, if found. /// [JsonPropertyName("block_with_signatures")] public BlockWithSignatures BlockWithSignatures { get; init; } + + [JsonPropertyName("block")] + public BlockV1Compat BlockV1 { get; init; } + } + + /// + /// Result for "chain_get_block" RPC response. + /// + [JsonConverter(typeof(GetBlockResultConverter))] + public class GetBlockResult : RpcResult + { + /// + /// The block. + /// + [JsonPropertyName("block")] + [JsonConverter(typeof(Block.BlockConverter))] + public IBlock Block { get; init; } + + /// + /// The proofs of the block, i.e. a collection of validators' signatures of the block hash. + /// + [JsonPropertyName("proofs")] + public List Proofs { get; init; } + + public class GetBlockResultConverter : JsonConverter + { + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert == typeof(GetBlockResult); + } + + public override GetBlockResult Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + var resultCompat = JsonSerializer.Deserialize(ref reader, options); + + if (resultCompat.BlockV1 != null) + { + return new GetBlockResult() + { + ApiVersion = resultCompat.ApiVersion, + Block = new BlockV1() + { + Hash = resultCompat.BlockV1.Hash, + Header = resultCompat.BlockV1.Header, + Body = resultCompat.BlockV1.Body, + }, + Proofs = resultCompat.BlockV1.Proofs, + }; + } + return new GetBlockResult() + { + ApiVersion = resultCompat.ApiVersion, + Block = resultCompat.BlockWithSignatures.Block, + Proofs = resultCompat.BlockWithSignatures.Proofs, + }; + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + GetBlockResult block, + JsonSerializerOptions options) + { + throw new JsonException($"not yet implemented"); + } + } } -} \ No newline at end of file +} diff --git a/Casper.Network.SDK/Types/Block.cs b/Casper.Network.SDK/Types/Block.cs index ba53a79..ba94174 100644 --- a/Casper.Network.SDK/Types/Block.cs +++ b/Casper.Network.SDK/Types/Block.cs @@ -344,28 +344,24 @@ public override IBlock Read( { try { + IBlock block; reader.Read(); var version = reader.GetString(); reader.Read(); switch (version) { case "Version1": - { - var block1 = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - block1._version = 1; - return block1; - } + block = JsonSerializer.Deserialize(ref reader, options); + break; case "Version2": - var block2 = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - block2._version = 2; - return block2; + block = JsonSerializer.Deserialize(ref reader, options); + break; default: throw new JsonException("Expected Version1 or Version2"); } - ; + reader.Read(); + return block; } catch (Exception e) { @@ -415,6 +411,11 @@ public class BlockV1 : Block /// [JsonPropertyName("body")] public BlockBodyV1 Body { get; init; } + + public BlockV1() + { + _version = 1; + } } /// @@ -433,6 +434,11 @@ public class BlockV2 : Block /// [JsonPropertyName("body")] public BlockBodyV2 Body { get; init; } + + public BlockV2() + { + _version = 2; + } } /// @@ -454,23 +460,4 @@ public class BlockProof [JsonConverter(typeof(Signature.SignatureConverter))] public Signature Signature { get; init; } } - - /// - /// A JSON-friendly representation of a block and the signatures for that block - /// - public class BlockWithSignatures - { - /// - /// The block. - /// - [JsonPropertyName("block")] - [JsonConverter(typeof(Block.BlockConverter))] - public IBlock Block { get; init; } - - /// - /// The proofs of the block, i.e. a collection of validators' signatures of the block hash. - /// - [JsonPropertyName("proofs")] - public List Proofs { get; init; } - } } \ No newline at end of file From aa52ee1cdf499444d784bab11c4b137ccd07baac Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 31 May 2024 12:58:50 +0200 Subject: [PATCH 027/126] parse chain_get_block_transfers response from Casper 1.x (backward compat) Signed-off-by: David Hernando --- Casper.Network.SDK/Types/Transfer.cs | 42 +++++++++++++++------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/Casper.Network.SDK/Types/Transfer.cs b/Casper.Network.SDK/Types/Transfer.cs index c6cf43b..59adf00 100644 --- a/Casper.Network.SDK/Types/Transfer.cs +++ b/Casper.Network.SDK/Types/Transfer.cs @@ -64,6 +64,11 @@ public class TransferV1 : Transfer [JsonPropertyName("to")] [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public AccountHashKey To { get; init; } + + public TransferV1() + { + _version = 1; + } } /// @@ -123,6 +128,12 @@ public class TransferV2 : Transfer [JsonPropertyName("to")] [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public AccountHashKey To { get; init; } + + + public TransferV2() + { + _version = 2; + } } public interface ITransfer @@ -172,28 +183,19 @@ public override ITransfer Read( { try { - reader.Read(); - var version = reader.GetString(); - reader.Read(); - switch (version) + using (JsonDocument doc = JsonDocument.ParseValue(ref reader)) { - case "Version1": - { - var transfer1 = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - transfer1._version = 1; - return transfer1; - } - case "Version2": - var transfer2 = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - transfer2._version = 2; - return transfer2; - default: - throw new JsonException("Expected Version1 or Version2"); - } + JsonElement root = doc.RootElement; + + if (root.TryGetProperty("Version1", out JsonElement v1Element)) + return JsonSerializer.Deserialize(v1Element.GetRawText(), options); - ; + if (root.TryGetProperty("Version2", out JsonElement v2Element)) + return JsonSerializer.Deserialize(v2Element.GetRawText(), options); + + // try as Casper node v1.x for backward compatibility + return JsonSerializer.Deserialize(root.GetRawText(), options); + } } catch (Exception e) { From 16407b1efd7543c83e4bb5e09200ad283ee8c590 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 31 May 2024 15:56:43 +0200 Subject: [PATCH 028/126] Backward Compat: unify TransferV1 and TransferV2 in one single class Transfer Signed-off-by: David Hernando --- .../ResultTypes/GetBlockTransfersResult.cs | 4 +- Casper.Network.SDK/Types/Transfer.cs | 109 +++++++++--------- 2 files changed, 57 insertions(+), 56 deletions(-) diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockTransfersResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockTransfersResult.cs index 05231b3..7658cb7 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockTransfersResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockTransfersResult.cs @@ -20,7 +20,7 @@ public class GetBlockTransfersResult : RpcResult /// The block's transfers /// [JsonPropertyName("transfers")] - [JsonConverter(typeof(GenericListConverter))] - public List Transfers { get; init; } + [JsonConverter(typeof(GenericListConverter))] + public List Transfers { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Transfer.cs b/Casper.Network.SDK/Types/Transfer.cs index 59adf00..5c81873 100644 --- a/Casper.Network.SDK/Types/Transfer.cs +++ b/Casper.Network.SDK/Types/Transfer.cs @@ -9,7 +9,7 @@ namespace Casper.Network.SDK.Types /// /// Represents a transfer from one purse to another /// - public class TransferV1 : Transfer + internal class TransferV1 { /// /// Transfer amount @@ -64,18 +64,16 @@ public class TransferV1 : Transfer [JsonPropertyName("to")] [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public AccountHashKey To { get; init; } - - public TransferV1() - { - _version = 1; - } } /// /// Represents a version 2 transfer from one purse to another /// - public class TransferV2 : Transfer + public class Transfer { + [JsonIgnore] + public int Version { get; init; } + /// /// Transfer amount /// @@ -128,55 +126,19 @@ public class TransferV2 : Transfer [JsonPropertyName("to")] [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public AccountHashKey To { get; init; } - - - public TransferV2() - { - _version = 2; - } - } - public interface ITransfer - { - public int Version { get; } - public TransferV1 TransferV1 { get; } - public TransferV2 TransferV2 { get; } - } - - public class Transfer: ITransfer - { - protected int _version; - - /// - /// Returns the version of the transfer. - /// - public int Version + public Transfer() { - get { return _version; } + // this value is overriden in FromTransferV1 + Version = 2; } - /// - /// Returns the transfer as a Version1 transfer object. - /// - TransferV1 ITransfer.TransferV1 => this as TransferV1; - - /// - /// Returns the transfer as a Version2 transfer object. - /// - TransferV2 ITransfer.TransferV2 => this as TransferV2; - /// /// Json converter class to serialize/deserialize a Block to/from Json /// - public class TransferConverter : JsonConverter + public class TransferConverter : JsonConverter { - public override bool CanConvert(Type typeToConvert) - { - return typeToConvert == typeof(ITransfer) || - typeToConvert == typeof(Transfer); - } - - public override ITransfer Read( + public override Transfer Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) @@ -188,13 +150,21 @@ public override ITransfer Read( JsonElement root = doc.RootElement; if (root.TryGetProperty("Version1", out JsonElement v1Element)) - return JsonSerializer.Deserialize(v1Element.GetRawText(), options); + { + var transfer = JsonSerializer.Deserialize(v1Element.GetRawText(), options); + return transfer != null ? FromTransferV1(transfer) : null; + } if (root.TryGetProperty("Version2", out JsonElement v2Element)) - return JsonSerializer.Deserialize(v2Element.GetRawText(), options); + { + var transfer = JsonSerializer.Deserialize(v2Element.GetRawText()); + return transfer; + } // try as Casper node v1.x for backward compatibility - return JsonSerializer.Deserialize(root.GetRawText(), options); + var transferv1 = JsonSerializer.Deserialize(root.GetRawText(), options); + return transferv1 != null ? FromTransferV1(transferv1) : null; + } } catch (Exception e) @@ -205,7 +175,7 @@ public override ITransfer Read( public override void Write( Utf8JsonWriter writer, - ITransfer transfer, + Transfer transfer, JsonSerializerOptions options) { switch (transfer.Version) @@ -213,13 +183,13 @@ public override void Write( case 1: writer.WritePropertyName("Version1"); writer.WriteStartObject(); - JsonSerializer.Serialize(transfer as TransferV1, options); + JsonSerializer.Serialize(ToTransferV1(transfer), options); writer.WriteEndObject(); break; case 2: writer.WritePropertyName("Version2"); writer.WriteStartObject(); - JsonSerializer.Serialize(transfer as TransferV2, options); + JsonSerializer.Serialize(transfer, options); writer.WriteEndObject(); break; default: @@ -227,5 +197,36 @@ public override void Write( } } } + + internal static Transfer FromTransferV1(TransferV1 v1) + { + return new Transfer() + { + Version = 1, + Amount = v1.Amount, + From = new InitiatorAddr { AccountHash = v1.From }, + To = v1.To, + Id = v1.Id, + TransactionHash = new TransactionHash() { Deploy = v1.DeployHash }, + Source = v1.Source, + Target = v1.Target, + Gas = v1.Gas, + }; + } + + internal static TransferV1 ToTransferV1(Transfer v1) + { + return new TransferV1() + { + Amount = v1.Amount, + From = v1.From.AccountHash, + To = v1.To, + Id = v1.Id, + DeployHash = v1.TransactionHash.Deploy, + Source = v1.Source, + Target = v1.Target, + Gas = v1.Gas, + }; + } } } \ No newline at end of file From 6ce5fced6494c03e18c892e84ae6ebe29543f6e7 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 3 Jun 2024 21:43:25 +0200 Subject: [PATCH 029/126] Renamed BlockV2 to Block. Implement cast operators to convert BlockV1 to BlockV2 for backwards compat. And back to V1. Signed-off-by: David Hernando --- .../JsonRpc/ResultTypes/GetBlockResult.cs | 17 +- Casper.Network.SDK/SSE/BlockAdded.cs | 2 +- Casper.Network.SDK/Types/Block.cs | 261 +++++++++--------- Casper.Network.SDK/Types/EraEnd.cs | 2 +- 4 files changed, 146 insertions(+), 136 deletions(-) diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs index ed4f29c..f1625bf 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBlockResult.cs @@ -31,7 +31,7 @@ internal class BlockWithSignatures /// [JsonPropertyName("block")] [JsonConverter(typeof(Block.BlockConverter))] - public IBlock Block { get; init; } + public Block Block { get; init; } /// /// The proofs of the block, i.e. a collection of validators' signatures of the block hash. @@ -63,7 +63,7 @@ public class GetBlockResult : RpcResult /// [JsonPropertyName("block")] [JsonConverter(typeof(Block.BlockConverter))] - public IBlock Block { get; init; } + public Block Block { get; init; } /// /// The proofs of the block, i.e. a collection of validators' signatures of the block hash. @@ -89,15 +89,16 @@ public override GetBlockResult Read( if (resultCompat.BlockV1 != null) { + var blockV1 = new BlockV1 + { + Hash = resultCompat.BlockV1.Hash, + Header = resultCompat.BlockV1.Header, + Body = resultCompat.BlockV1.Body, + }; return new GetBlockResult() { ApiVersion = resultCompat.ApiVersion, - Block = new BlockV1() - { - Hash = resultCompat.BlockV1.Hash, - Header = resultCompat.BlockV1.Header, - Body = resultCompat.BlockV1.Body, - }, + Block = (Block)blockV1, Proofs = resultCompat.BlockV1.Proofs, }; } diff --git a/Casper.Network.SDK/SSE/BlockAdded.cs b/Casper.Network.SDK/SSE/BlockAdded.cs index a0df706..54d1095 100644 --- a/Casper.Network.SDK/SSE/BlockAdded.cs +++ b/Casper.Network.SDK/SSE/BlockAdded.cs @@ -19,6 +19,6 @@ public class BlockAdded /// [JsonPropertyName("block")] [JsonConverter(typeof(Block.BlockConverter))] - public IBlock Block { get; init; } + public Block Block { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Block.cs b/Casper.Network.SDK/Types/Block.cs index ba94174..c94ff6a 100644 --- a/Casper.Network.SDK/Types/Block.cs +++ b/Casper.Network.SDK/Types/Block.cs @@ -1,78 +1,17 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; using Org.BouncyCastle.Asn1.X509.Qualified; namespace Casper.Network.SDK.Types { - public abstract class BlockHeader - { - /// - /// Json converter class to serialize/deserialize a Block to/from Json - /// - public class BlockHeaderConverter : JsonConverter - { - public override BlockHeader Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - try - { - reader.Read(); - var version = reader.GetString(); - reader.Read(); - switch (version.ToLowerInvariant()) - { - case "version1": - { - var block1 = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - return block1; - } - case "version2": - var block2 = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - return block2; - default: - throw new JsonException("Expected Version1 or Version2"); - } - } - catch (Exception e) - { - throw new JsonException(e.Message); - } - } - - public override void Write( - Utf8JsonWriter writer, - BlockHeader blockHeader, - JsonSerializerOptions options) - { - if (blockHeader is BlockHeaderV2) - { - writer.WritePropertyName("Version2"); - writer.WriteStartObject(); - JsonSerializer.Serialize(blockHeader as BlockHeaderV2, options); - writer.WriteEndObject(); - } - else - { - writer.WritePropertyName("Version1"); - writer.WriteStartObject(); - JsonSerializer.Serialize(blockHeader as BlockHeaderV1, options); - writer.WriteEndObject(); - } - } - } - } - - /// + /// /// A block header /// - public class BlockHeaderV1 : BlockHeader + public class BlockHeaderV1 { /// /// A seed needed for initializing a future era. @@ -135,7 +74,7 @@ public class BlockHeaderV1 : BlockHeader public string Timestamp { get; init; } } - public class BlockHeaderV2 : BlockHeaderV1 + public class BlockHeader : BlockHeaderV1 { /// /// The `EraEnd` of a block if it is a switch block. @@ -148,6 +87,16 @@ public class BlockHeaderV2 : BlockHeaderV1 /// [JsonPropertyName("current_gas_price")] public UInt16 CurrentGasPrice { get; init; } + + /// + /// Public key of the validator that proposed the block + /// + [JsonPropertyName("proposer")] + [JsonConverter(typeof(Proposer.ProposerConverter))] + public Proposer Proposer { get; init; } + + [JsonPropertyName("last_switch_block_hash")] + public string LastSwitchBlockHash { get; init; } } /// @@ -218,11 +167,6 @@ public override void Write( /// public class BlockBodyV1 { - public virtual uint Version - { - get { return 1; } - } - /// /// The deploy hashes of the non-transfer deploys within the block. /// @@ -246,7 +190,7 @@ public virtual uint Version /// /// A block body /// - public class BlockBodyV2 + public class BlockBody { /// /// The hashes of the installer/upgrader transactions within the block. @@ -266,13 +210,6 @@ public class BlockBodyV2 [JsonPropertyName("standard")] public List Standard { get; init; } - /// - /// Public key of the validator that proposed the block - /// - [JsonPropertyName("proposer")] - [JsonConverter(typeof(Proposer.ProposerConverter))] - public Proposer Proposer { get; init; } - /// /// The hashes of the mint transactions within the block. /// @@ -289,16 +226,7 @@ public class BlockBodyV2 public List> RewardedSignatures { get; init; } } - public interface IBlock - { - public int Version { get; } - public BlockV1 BlockV1 { get; } - public BlockV2 BlockV2 { get; } - - public string Hash { get; init; } - } - - public abstract class Block : IBlock + public class Block { protected int _version; @@ -309,59 +237,82 @@ public int Version { get { return _version; } } + + protected BlockV1 _blockV1; + + // Explicit cast operator from A to B + public static explicit operator BlockV1(Block block) + { + if(block._version == 1) + return block._blockV1; + + throw new InvalidCastException("Version2 block cannot be converted to Version1"); + } + + public static explicit operator Block(BlockV1 block) + { + return new Block + { + _version = 1, + _blockV1 = block, + Hash = block.Hash, + Header = BlockV1.BlockHeaderFromV1(block.Header, block.Body), + Body = BlockV1.BlockBodyFromV1(block.Body), + }; + } /// - /// Returns the block as a Version1 block object. + /// Block hash /// - BlockV1 IBlock.BlockV1 => this as BlockV1; + [JsonPropertyName("hash")] + public string Hash { get; init; } /// - /// Returns the block as a Version2 block object. + /// Block header /// - BlockV2 IBlock.BlockV2 => this as BlockV2; + [JsonPropertyName("header")] + public BlockHeader Header { get; init; } /// - /// Block hash + /// Block body /// - [JsonPropertyName("hash")] - public string Hash { get; init; } - + [JsonPropertyName("body")] + public BlockBody Body { get; init; } + /// /// Json converter class to serialize/deserialize a Block to/from Json /// - public class BlockConverter : JsonConverter + public class BlockConverter : JsonConverter { - public override bool CanConvert(Type typeToConvert) - { - return typeToConvert == typeof(IBlock) || - typeToConvert == typeof(Block); - } - - public override IBlock Read( + public override Block Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { try { - IBlock block; reader.Read(); var version = reader.GetString(); reader.Read(); switch (version) { case "Version1": - block = JsonSerializer.Deserialize(ref reader, options); - break; + var blockv1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return (Block)blockv1; case "Version2": - block = JsonSerializer.Deserialize(ref reader, options); - break; + var blockv2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new Block + { + _version = 2, + Hash = blockv2.Hash, + Header = blockv2.Header, + Body = blockv2.Body, + }; default: throw new JsonException("Expected Version1 or Version2"); } - - reader.Read(); - return block; } catch (Exception e) { @@ -371,7 +322,7 @@ public override IBlock Read( public override void Write( Utf8JsonWriter writer, - IBlock block, + Block block, JsonSerializerOptions options) { switch (block.Version) @@ -379,13 +330,13 @@ public override void Write( case 1: writer.WritePropertyName("Version1"); writer.WriteStartObject(); - JsonSerializer.Serialize(block as BlockV1, options); + JsonSerializer.Serialize((BlockV1)block, options); writer.WriteEndObject(); break; case 2: writer.WritePropertyName("Version2"); writer.WriteStartObject(); - JsonSerializer.Serialize(block as BlockV2, options); + JsonSerializer.Serialize(block, options); writer.WriteEndObject(); break; default: @@ -398,8 +349,14 @@ public override void Write( /// /// A block in the network /// - public class BlockV1 : Block + public class BlockV1 { + /// + /// Block hash + /// + [JsonPropertyName("hash")] + public string Hash { get; init; } + /// /// Block header /// @@ -412,33 +369,85 @@ public class BlockV1 : Block [JsonPropertyName("body")] public BlockBodyV1 Body { get; init; } - public BlockV1() + internal static BlockHeader BlockHeaderFromV1(BlockHeaderV1 header, BlockBodyV1 body) { - _version = 1; + Dictionary> rewards = new(); + if (header.EraEnd != null && header.EraEnd.EraReport.Rewards.Count > 0) + { + foreach (var r in header.EraEnd.EraReport.Rewards) + { + rewards.Add(r.PublicKey.ToString().ToLower(), + new List { r.Amount.ToString()}); + } + } + + EraEndV2 eraEnd = header.EraEnd == null ? null : new EraEndV2 + { + NextEraValidatorWeights = header.EraEnd.NextEraValidatorWeights, + NextEraGasPrice = 1, + Equivocators = header.EraEnd.EraReport.Equivocators, + InactiveValidators = header.EraEnd.EraReport.InactiveValidators, + Rewards = rewards, + }; + + return new BlockHeader + { + BodyHash = header.BodyHash, + ParentHash = header.ParentHash, + Height = header.Height, + Timestamp = header.Timestamp, + EraId = header.EraId, + RandomBit = header.RandomBit, + AccumulatedSeed = header.AccumulatedSeed, + StateRootHash = header.StateRootHash, + ProtocolVersion = header.ProtocolVersion, + CurrentGasPrice = 1, + LastSwitchBlockHash = null, + Proposer = body.Proposer, + EraEnd = eraEnd, + }; + } + + internal static BlockBody BlockBodyFromV1(BlockBodyV1 body) + { + var mintTransactions = + body.TransferHashes.Select(t => new TransactionHash { Deploy = t }).ToList(); + var standardTransactions = + body.DeployHashes.Select(t => new TransactionHash { Deploy = t }).ToList(); + + return new BlockBody + { + Auction = new List(), + Mint = mintTransactions, + Standard = standardTransactions, + InstallUpgrade = new List(), + RewardedSignatures = null, + }; } } /// /// A block in the network /// - public class BlockV2 : Block + internal class BlockV2 { + /// + /// Block hash + /// + [JsonPropertyName("hash")] + public string Hash { get; init; } + /// /// Block header /// [JsonPropertyName("header")] - public BlockHeaderV2 Header { get; init; } + public BlockHeader Header { get; init; } /// /// Block body /// [JsonPropertyName("body")] - public BlockBodyV2 Body { get; init; } - - public BlockV2() - { - _version = 2; - } + public BlockBody Body { get; init; } } /// diff --git a/Casper.Network.SDK/Types/EraEnd.cs b/Casper.Network.SDK/Types/EraEnd.cs index 9a1d32b..38bdabd 100644 --- a/Casper.Network.SDK/Types/EraEnd.cs +++ b/Casper.Network.SDK/Types/EraEnd.cs @@ -100,7 +100,7 @@ public class EraEndV2 /// The rewards distributed to the validators. /// [JsonPropertyName("rewards")] - public Dictionary Rewards { get; init; } + public Dictionary> Rewards { get; init; } /// /// Next Era gas price From 16661537fb2e4b506a811059caf93a0c9ccab94f Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 5 Jun 2024 12:05:27 +0200 Subject: [PATCH 030/126] refactor Transfer casting to follow same approach as the implementation of Block and Deploy castings. Signed-off-by: David Hernando --- Casper.Network.SDK/Types/Transfer.cs | 76 ++++++++++++++-------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/Casper.Network.SDK/Types/Transfer.cs b/Casper.Network.SDK/Types/Transfer.cs index 5c81873..e35890d 100644 --- a/Casper.Network.SDK/Types/Transfer.cs +++ b/Casper.Network.SDK/Types/Transfer.cs @@ -9,7 +9,7 @@ namespace Casper.Network.SDK.Types /// /// Represents a transfer from one purse to another /// - internal class TransferV1 + public class TransferV1 { /// /// Transfer amount @@ -71,8 +71,40 @@ internal class TransferV1 /// public class Transfer { + protected int _version; + [JsonIgnore] - public int Version { get; init; } + public int Version + { + get { return _version; } + } + + protected TransferV1 _transferV1; + + public static explicit operator TransferV1(Transfer transfer) + { + if(transfer._version == 1) + return transfer._transferV1; + + throw new InvalidCastException("Version2 transfer cannot be converted to Version1"); + } + + public static explicit operator Transfer(TransferV1 transfer) + { + return new Transfer() + { + _version = 1, + _transferV1 = transfer, + Amount = transfer.Amount, + From = new InitiatorAddr { AccountHash = transfer.From }, + To = transfer.To, + Id = transfer.Id, + TransactionHash = new TransactionHash() { Deploy = transfer.DeployHash }, + Source = transfer.Source, + Target = transfer.Target, + Gas = transfer.Gas, + }; + } /// /// Transfer amount @@ -130,7 +162,7 @@ public class Transfer public Transfer() { // this value is overriden in FromTransferV1 - Version = 2; + _version = 2; } /// @@ -152,7 +184,7 @@ public override Transfer Read( if (root.TryGetProperty("Version1", out JsonElement v1Element)) { var transfer = JsonSerializer.Deserialize(v1Element.GetRawText(), options); - return transfer != null ? FromTransferV1(transfer) : null; + return transfer != null ? (Transfer)transfer : null; } if (root.TryGetProperty("Version2", out JsonElement v2Element)) @@ -163,8 +195,7 @@ public override Transfer Read( // try as Casper node v1.x for backward compatibility var transferv1 = JsonSerializer.Deserialize(root.GetRawText(), options); - return transferv1 != null ? FromTransferV1(transferv1) : null; - + return transferv1 != null ? (Transfer)transferv1 : null; } } catch (Exception e) @@ -183,7 +214,7 @@ public override void Write( case 1: writer.WritePropertyName("Version1"); writer.WriteStartObject(); - JsonSerializer.Serialize(ToTransferV1(transfer), options); + JsonSerializer.Serialize((TransferV1)transfer, options); writer.WriteEndObject(); break; case 2: @@ -197,36 +228,5 @@ public override void Write( } } } - - internal static Transfer FromTransferV1(TransferV1 v1) - { - return new Transfer() - { - Version = 1, - Amount = v1.Amount, - From = new InitiatorAddr { AccountHash = v1.From }, - To = v1.To, - Id = v1.Id, - TransactionHash = new TransactionHash() { Deploy = v1.DeployHash }, - Source = v1.Source, - Target = v1.Target, - Gas = v1.Gas, - }; - } - - internal static TransferV1 ToTransferV1(Transfer v1) - { - return new TransferV1() - { - Amount = v1.Amount, - From = v1.From.AccountHash, - To = v1.To, - Id = v1.Id, - DeployHash = v1.TransactionHash.Deploy, - Source = v1.Source, - Target = v1.Target, - Gas = v1.Gas, - }; - } } } \ No newline at end of file From 565c75ba5841abdc70c1857ae16d554bdef73853 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 11 Jun 2024 14:14:53 +0200 Subject: [PATCH 031/126] CSDK-175 updated BlockAdded parsing for backwards compatibility. Added unit tests. Signed-off-by: David Hernando --- Casper.Network.SDK.Test/AssertExtensions.cs | 34 ++++++ .../RPCResponses/GetBlockResultTest.cs | 54 ++++++++ .../TestData/get-block-result-v156.json | 65 ++++++++++ .../TestData/get-block-result-v200.json | 115 ++++++++++++++++++ Casper.Network.SDK/SSE/BlockAdded.cs | 44 ++++++- 5 files changed, 311 insertions(+), 1 deletion(-) create mode 100644 Casper.Network.SDK.Test/AssertExtensions.cs create mode 100644 Casper.Network.SDK.Test/RPCResponses/GetBlockResultTest.cs create mode 100644 Casper.Network.SDK.Test/TestData/get-block-result-v156.json create mode 100644 Casper.Network.SDK.Test/TestData/get-block-result-v200.json diff --git a/Casper.Network.SDK.Test/AssertExtensions.cs b/Casper.Network.SDK.Test/AssertExtensions.cs new file mode 100644 index 0000000..e04b538 --- /dev/null +++ b/Casper.Network.SDK.Test/AssertExtensions.cs @@ -0,0 +1,34 @@ +using NUnit.Framework; +using Org.BouncyCastle.Utilities.Encoders; + +namespace NetCasperTest +{ + public static class AssertExtensions + { + public static void IsHash(string maybeHash) + { + try + { + Assert.AreEqual(32, Hex.Decode(maybeHash).Length); + } + catch + { + Assert.Fail($"Cannot decode a hash from '{maybeHash}'"); + } + } + + public static void IsValidHex(string maybeHex, uint bytesLength = 0) + { + try + { + var bytes = Hex.Decode(maybeHex); + if(bytesLength > 0) + Assert.AreEqual(bytesLength, bytes.Length); + } + catch + { + Assert.Fail($"Cannot decode hex bytes from '{maybeHex}'"); + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/RPCResponses/GetBlockResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetBlockResultTest.cs new file mode 100644 index 0000000..d216f23 --- /dev/null +++ b/Casper.Network.SDK.Test/RPCResponses/GetBlockResultTest.cs @@ -0,0 +1,54 @@ +using System.IO; +using Casper.Network.SDK.JsonRpc.ResultTypes; +using Casper.Network.SDK.Types; +using NUnit.Framework; +using Org.BouncyCastle.Utilities.Encoders; + +namespace NetCasperTest.RPCResponses +{ + public class GetBlockResultTest + { + [Test] + public void GetBlockResultTest_v156() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-block-result-v156.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("1.5.6", result.ApiVersion); + Assert.AreEqual(3, result.Proofs.Count); + Assert.IsNotNull(result.Proofs[1].PublicKey); + AssertExtensions.IsValidHex(result.Proofs[1].Signature.ToString(), 65); + Assert.AreEqual(1, result.Block.Version); + AssertExtensions.IsHash(result.Block.Hash); + Assert.AreEqual(2947381, result.Block.Header.Height); + Assert.AreEqual(13670, result.Block.Header.EraId); + Assert.IsNull(result.Block.Header.EraEnd); + } + + [Test] + public void GetBlockResultTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-block-result-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + Assert.AreEqual(5, result.Proofs.Count); + Assert.IsNotNull(result.Proofs[1].PublicKey); + AssertExtensions.IsValidHex(result.Proofs[1].Signature.ToString(), 65); + Assert.AreEqual(2, result.Block.Version); + AssertExtensions.IsHash(result.Block.Hash); + Assert.AreEqual(1551, result.Block.Header.Height); + Assert.AreEqual(141, result.Block.Header.EraId); + Assert.IsNotNull(result.Block.Header.EraEnd); + var eraEnd = result.Block.Header.EraEnd; + Assert.IsTrue(eraEnd.NextEraValidatorWeights.Count > 1); + Assert.IsNotNull(eraEnd.NextEraValidatorWeights[1].PublicKey); + Assert.IsNotNull(eraEnd.NextEraValidatorWeights[1].Weight); + Assert.IsTrue(eraEnd.Rewards.Count > 1); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/get-block-result-v156.json b/Casper.Network.SDK.Test/TestData/get-block-result-v156.json new file mode 100644 index 0000000..0545bf1 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-block-result-v156.json @@ -0,0 +1,65 @@ +{ + "api_version": "1.5.6", + "block": { + "hash": "36115bdee7ace197d200fc1b61ebd69e689bc1bbe0b7664f3cb55f66bf4590ed", + "header": { + "parent_hash": "8c870bfceb9522f8fb183b7c57443d3735b69afe377a0d4ebd94196fa8d80771", + "state_root_hash": "c354134eb17e4f73411c1950e7a3b566f4900b10ff351c1b1f097a096f8d9b90", + "body_hash": "577a030327479c1451ae5f882e4b79cc3570d9cb967b8e72f78006dba1a475f2", + "random_bit": true, + "accumulated_seed": "ce636433c38c9d1754fd3586f97437997773f5cfaa21d355ef5780f7bd42960e", + "era_end": null, + "timestamp": "2024-05-15T09:42:12.736Z", + "era_id": 13670, + "height": 2947381, + "protocol_version": "1.5.6" + }, + "body": { + "proposer": "01d2b6b397578ed3d46ebd94525a96b787d767494d94496a68b645a8bc21b66806", + "deploy_hashes": [ + "2dcf4d1b6990e725bf2a5075c360faa74a6f330c74afdf5dd06b09dbe3c16133", + "92b2545727df4f1bf6876cb6ce1b8cc9bf435b46f4d33c8fd057c457b7cc0adb", + "18d00684d17198141735abc4620466eac958da162caa76294ab531a60dfa9135", + "9eb5dd22e9785b0356b9c7ce172dd9c0d41d143e926690b9a6c40e9e628e5055", + "13fe9581cc4985b4cf1e1cdc5d759b4de3fcc07485742a0f67f86a1534919418", + "48abbe7f5c0ff6bee97a268d087ea4922f56e187ef5cd1bf6f581c39e5fb9791", + "307ad4761d2f350b3b01ed3b9d37feb05a5cc20ec38ae0f3a69280d3162fa676", + "81b8b21e4e573db18f24360596c5492fc1612d4daf96c8dbde3604cbb6cd4aa2", + "8c44f729fcd5b68904acb5bee80f3f900984e50ad8e88731540ee2a44e1bba12", + "543fd268a3340a1f94e3eb2bd282c427e16b82b0c400f1f674e6978366b1ad8d", + "7f0d7e260c9e325dd81a0300ecb3d8809b41c030187df8105e6ba954d9853918", + "2956b8b0ef0fd987ab18b0e3b9ee8dd1b2e1dafe6d5425b1e5a449347d1e75a6", + "a10be1850ae501564ada0075701242062f68270df51612697aa962ca0c037313", + "0b8a9067db06fb58a1d1d56a561bcc1906cd18c834a6d9de7d14dbe99be5259c", + "b47f979afa2c5cea563ab949bd6fc80032787e93530576dde233cb77eca936d9", + "3ff8291a5042b8c70ab8044e5f0ed177b32e8f6ddb6be9c7c39542a9a0e8a14b", + "23b4735ecce1923677be79251e6e76e4b5c6659f2b0d14a2fd16a81d8833163c", + "335ee7c3405d73129444f986700e1b7244069618b6f30a30ef7c3054bdd19c6c", + "378db7e92e65cd84dd6a61c7f9c01acb45bac7817fbdb23a600d5301d9a9f9eb", + "1cb75e19e48405ce3df81c7e6ce7158bc97c7579ad4d8b77f1faf0f070a00a29", + "800f1ecb41fc5f1b00775bd96967090f0a29fae0c4e2daff1ec1c7fb2f3261d9", + "550888c9b56f345093d5210ed2f69c284ff05f2a5a04c7f6e483edf0616fdcc1", + "05d4fba4fa790e3058f3893b6c9b5731be4c92710262401b82fd3781cf618b12", + "7e44ecfeabe496dae657958e870777ad6f677b01686428b3f7aa02518d0a6c05", + "fbfcb832613adcb5ed2d3125d95ae20e19499113435696c4d0c40ddd7312aaf3" + ], + "transfer_hashes": [ + "31849e17f715273ad7032d51534c5b6029dd0dec1e01c225c50083752a750219" + ] + }, + "proofs": [ + { + "public_key": "01000e6fce753895c0d08d5d6af62db4e9b0d070f10e69e2c6badf977b29bbeeee", + "signature": "011836119151750cc8847e145d1472ae6ee68c107d32cb838087a1f9f3ad2089a8f9697141563018df611bda7eb05ec34abe708043d28b2edcd025520153051108" + }, + { + "public_key": "0100a8faa48e4b20966105c610d8a5f80c4248b337686c51213297d88afe0ff84e", + "signature": "013baa9370748764e8020f9e07c2f43e1bebc470b4742cf020507ad7d41aa8a24bda12c026bd561911874a2c46fc2aaea2aba727bcea20766031972e4c3d666a08" + }, + { + "public_key": "01026ca707c348ed8012ac6a1f28db031fadd6eb67203501a353b867a08c8b9a80", + "signature": "0127e72f44e3027a3393a4720e6c1f441d8ad3a343372897ec7e88b30bbc2c62c882c246fa5077aa1eeb0c3e1ca105dd1b753a1b0be109cbe1fadc96cf8196820f" + } + ] + } +} diff --git a/Casper.Network.SDK.Test/TestData/get-block-result-v200.json b/Casper.Network.SDK.Test/TestData/get-block-result-v200.json new file mode 100644 index 0000000..a84015a --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-block-result-v200.json @@ -0,0 +1,115 @@ +{ + "api_version": "2.0.0", + "block_with_signatures": { + "block": { + "Version2": { + "hash": "8c1042f7e78c58229b9cf8579a2a5094e68332646b433bd785650d2a4f607421", + "header": { + "parent_hash": "b877088ae6272d77fc1b4b936331d9cef4c2b6e7ea2e8854b5ee8e93c438cadd", + "state_root_hash": "972ff7268bee4e6a11c0b1c615dfcc1ed89f13de2f19c1b85a0d9dcf95774eef", + "body_hash": "2f6218cdaa056e391fc9fb37f2dae71eefa3ca03e91d7fc4455bb6a4cd01ac5b", + "random_bit": false, + "accumulated_seed": "dacf81815b307edc02802bb8afee014cd2681749efeaab5a4f5e0a1240e26663", + "era_end": { + "equivocators": [], + "inactive_validators": [], + "next_era_validator_weights": [ + { + "validator": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "weight": "1457007299027515530" + }, + { + "validator": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "weight": "2462811880650517518" + }, + { + "validator": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "weight": "684867946138376005" + }, + { + "validator": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "weight": "1500458509683331484" + }, + { + "validator": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "weight": "1604854364500260181" + } + ], + "rewards": { + "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6": [ + "3787954951771805", + "378861119683192" + ], + "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab": [ + "18392670705238418", + "638104246294617" + ], + "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba": [ + "1760738793374719", + "176836068103231" + ], + "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6": [ + "9901127257757664", + "391012293027695" + ], + "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b": [ + "18657508291857411", + "915186272891264" + ] + }, + "next_era_gas_price": 1 + }, + "timestamp": "2024-06-10T13:44:09.123Z", + "era_id": 141, + "height": 1551, + "protocol_version": "2.0.0", + "proposer": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "current_gas_price": 1, + "last_switch_block_hash": "45216a334c0ef0e886ca2671d5795150e8bc0f1a1a47719112d7289dbb5e5dfd" + }, + "body": { + "transactions": { + "2": [ + { + "Version1": "82bedd65735508daeb26e9132f24bd8861bcece1120ee945a3362ef6526e3884" + } + ] + }, + "rewarded_signatures": [ + [ + 248 + ], + [ + 0 + ], + [ + 0 + ] + ] + } + } + }, + "proofs": [ + { + "public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "signature": "01bceb8d8ebdec1065d0a9614b601b2621a3f127bfad6dce4c5d106d7fdabf98e0e5824309fb907c51071060dce62cc445330b94298dc2cccc4a49cb16e9150103" + }, + { + "public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "signature": "01682c1cf7770ced1b9e764db6223c4b0cb349b14979ea67c89e5af9b5e325f0c2a56b7844e136c349475888b334acf9ba803b0c64a86146db4d6801222a336a0a" + }, + { + "public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "signature": "0108ddabc990a3b01628f52bdcbbc200d2086e9c5c3e8e6c1ce1e6aa28d4cf41183161a0ebb72243d56dc6a6deff028b16c10970b84bdd8fee5c8cea73b8fcf201" + }, + { + "public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "signature": "01713456c93fb75ebd05fcbc6c3eab28e04a19da43bb056d935a9af6a7781487468bc2127c8c49fe794fcb61aae6552893281d91fd6ea9be8c8530c51d41ff1508" + }, + { + "public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "signature": "01113063714eed166f636b4437bc839fa4c26d59fc3fdf8268f3ee6466e559521edad9737637c5918adb713b4281e2f761e595189be086fcabd50d945d8d39f106" + } + ] + } +} diff --git a/Casper.Network.SDK/SSE/BlockAdded.cs b/Casper.Network.SDK/SSE/BlockAdded.cs index 54d1095..aae03db 100644 --- a/Casper.Network.SDK/SSE/BlockAdded.cs +++ b/Casper.Network.SDK/SSE/BlockAdded.cs @@ -1,8 +1,11 @@ +using System; +using System.Text.Json; using System.Text.Json.Serialization; using Casper.Network.SDK.Types; namespace Casper.Network.SDK.SSE { + /// /// A Block that has been added to the linear chain and stored in the node. /// @@ -18,7 +21,46 @@ public class BlockAdded /// The Block data. /// [JsonPropertyName("block")] - [JsonConverter(typeof(Block.BlockConverter))] + [JsonConverter(typeof(BlockConverter))] public Block Block { get; init; } + + public class BlockConverter : JsonConverter + { + public override Block Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + using (JsonDocument document = JsonDocument.ParseValue(ref reader)) + { + if (document.RootElement.TryGetProperty("header", out var header)) + { + var blockV1 = JsonSerializer.Deserialize(document.RootElement.GetRawText()); + return (Block)blockV1; + } + + var blockSerializerOptions = new JsonSerializerOptions(options); + blockSerializerOptions.Converters.Add(new Block.BlockConverter()); + var block = JsonSerializer.Deserialize(document.RootElement.GetRawText(), blockSerializerOptions); + return block; + } + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + Block block, + JsonSerializerOptions options) + { + throw new JsonException( + "Cannot serialize a block with this converter. Use Block.BlockConverter instead"); + } + } } } \ No newline at end of file From ad1666d57d6265f1152eb8838004d597f66bd6e0 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 11 Jun 2024 18:43:24 +0200 Subject: [PATCH 032/126] added tests. Signed-off-by: David Hernando --- .../RPCResponses/GetNodeStatusResultTest.cs | 32 +++++++++++++ .../TestData/info-get-status-v200.json | 47 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 Casper.Network.SDK.Test/RPCResponses/GetNodeStatusResultTest.cs create mode 100644 Casper.Network.SDK.Test/TestData/info-get-status-v200.json diff --git a/Casper.Network.SDK.Test/RPCResponses/GetNodeStatusResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetNodeStatusResultTest.cs new file mode 100644 index 0000000..c079bad --- /dev/null +++ b/Casper.Network.SDK.Test/RPCResponses/GetNodeStatusResultTest.cs @@ -0,0 +1,32 @@ +using System.IO; +using Casper.Network.SDK.JsonRpc.ResultTypes; +using Casper.Network.SDK.Types; +using NUnit.Framework; +using Org.BouncyCastle.Utilities.Encoders; + +namespace NetCasperTest.RPCResponses +{ + public class GetNodeStatusResultTest + { + [Test] + public void GetBlockResultTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/info-get-status-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + Assert.AreEqual(4, result.Peers.Count); + Assert.IsNotNull(result.Peers[1].NodeId); + Assert.IsNotNull(result.Peers[1].Address); + Assert.IsTrue(result.BuildVersion.StartsWith("2.0.0-")); + Assert.AreEqual("2.0.0-f803ee53d", result.BuildVersion); + Assert.AreEqual("casper-net-1", result.ChainspecName); + Assert.AreEqual("7ca5855e14e936019193d07055fb3390e21ec8cb75724ea756b4eac84e1b5fd7", result.StartingStateRootHash.ToLower()); + Assert.AreEqual("01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", result.OurPublicSigningKey.ToString().ToLower()); + Assert.AreEqual("643f9d06cebd07dffef47b7bfa70cc014496ff904561a78a1b5e8123247d3231", result.LatestSwitchBlockHash.ToLower()); + Assert.AreEqual("89ceb389e0e8244d8f82afacedaccb42d256831c3a036b06c0770323905d97d1", result.LastAddedBlockInfo.Hash.ToLower()); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/info-get-status-v200.json b/Casper.Network.SDK.Test/TestData/info-get-status-v200.json new file mode 100644 index 0000000..da612fe --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/info-get-status-v200.json @@ -0,0 +1,47 @@ +{ + "api_version": "2.0.0", + "peers": [ + { + "node_id": "tls:0eae..e79e", + "address": "127.0.0.1:22103" + }, + { + "node_id": "tls:151f..dab7", + "address": "127.0.0.1:22104" + }, + { + "node_id": "tls:3e34..67cc", + "address": "127.0.0.1:22102" + }, + { + "node_id": "tls:64d8..4609", + "address": "127.0.0.1:22105" + } + ], + "build_version": "2.0.0-f803ee53d", + "chainspec_name": "casper-net-1", + "starting_state_root_hash": "7ca5855e14e936019193d07055fb3390e21ec8cb75724ea756b4eac84e1b5fd7", + "last_added_block_info": { + "hash": "89ceb389e0e8244d8f82afacedaccb42d256831c3a036b06c0770323905d97d1", + "timestamp": "2024-06-11T16:32:33.703Z", + "era_id": 688, + "height": 7540, + "state_root_hash": "2177c9546bd31b428f0862e2845e46376e4d66fe6935b3e9eba33be67692a6b9", + "creator": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba" + }, + "our_public_signing_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "round_length": "4s 96ms", + "next_upgrade": null, + "uptime": "7h 59m", + "reactor_state": "Validate", + "last_progress": "2024-06-11T07:51:41.458Z", + "available_block_range": { + "low": 0, + "high": 7540 + }, + "block_sync": { + "historical": null, + "forward": null + }, + "latest_switch_block_hash": "643f9d06cebd07dffef47b7bfa70cc014496ff904561a78a1b5e8123247d3231" +} From c139e0d8018f41818beaa823d5bf87db01c95842 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 11 Jun 2024 18:55:18 +0200 Subject: [PATCH 033/126] added unit tests. Signed-off-by: David Hernando --- .../QueryBalanceDetailsResultTest.cs | 29 +++++++++++++ .../TestData/query-balance-details-v200.json | 43 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 Casper.Network.SDK.Test/RPCResponses/QueryBalanceDetailsResultTest.cs create mode 100644 Casper.Network.SDK.Test/TestData/query-balance-details-v200.json diff --git a/Casper.Network.SDK.Test/RPCResponses/QueryBalanceDetailsResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/QueryBalanceDetailsResultTest.cs new file mode 100644 index 0000000..f85cbb4 --- /dev/null +++ b/Casper.Network.SDK.Test/RPCResponses/QueryBalanceDetailsResultTest.cs @@ -0,0 +1,29 @@ +using System.IO; +using Casper.Network.SDK.JsonRpc.ResultTypes; +using Casper.Network.SDK.Types; +using NUnit.Framework; +using Org.BouncyCastle.Utilities.Encoders; + +namespace NetCasperTest.RPCResponses +{ + public class GetNodeStatusResultTest + { + [Test] + public void GetBlockResultTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/query-balance-details-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + Assert.AreEqual(7, result.Holds.Count); + Assert.IsTrue(result.Holds[1].Amount > 0); + Assert.IsTrue(result.Holds[1].Time > 0); + Assert.IsFalse(string.IsNullOrWhiteSpace(result.Holds[1].Proof)); + Assert.IsTrue(result.TotalBalance > 0); + Assert.IsTrue(result.AvailableBalance > 0); + Assert.IsFalse(string.IsNullOrWhiteSpace(result.TotalBalanceProof)); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/query-balance-details-v200.json b/Casper.Network.SDK.Test/TestData/query-balance-details-v200.json new file mode 100644 index 0000000..7bb9585 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/query-balance-details-v200.json @@ -0,0 +1,43 @@ +{ + "api_version": "2.0.0", + "total_balance": "999999999999999999999937500000000", + "available_balance": "999999999999999999997937499950000", + "total_balance_proof": "010000000642848ec03c268eb7b69fe729a834db06dc3b182055fb00e5d4a066cfc1118298000f0000000e00afb572fb5ac138938d44c64d3108020000000042250000000c006fc8c4863bd73ab8a33c9ff46c2b3f0611c312b23ba9de8ffcaaaf940cfb9b770e004d419e30142fdd2da42df93349891f54190a21a93b4b9fdfff8222fbb6348c5610009c65771c2232c1bb0270b247ba6af09fab7c2cb9d78b42de94daf097d4117a381100d79c23c82f5f12519e80d22af2233fd9f7b4c31b5bab900fba7af37b096da98b13009c2e309b248e6e247b4beaa917058da8b189bf03f26c95fbf394ba57c34e228f1a01960ca68f3138a882eae83e2e6554c8068634fac02fa1c2a805fa5cb15d542aae250039058b91411598a541d2108b61cb2d1369ff65c6d3fa9c1639ea73b74b3fa5872f00f2b4b0a07af15aa6cd303e317d89046aecb0033f2d06f78fef2c7c7d33775394320047991ecf1aad64080af41fbaff781ed6785b5fa2969490fce17595d1497c6c6b340065f0cc526a2b698a0010df3f8a5ecf3737d9c6564e6eb14774345951fded5634480055ca4b5de7e379e1f04ceb66af55e4229e4f169254395596630ed2728c136c4d5500cbe030b09d1bab294d7cdf5292d8ccda7e658b480b6bfe9b267a729b5889099e5a00090adb5ca41d14b879ac98eac90ebf2f38351b92a773566de6b6cadc56833b6d5c00f56647832ce5d0d67ab576a82876e789dd2e3a11831f9840092610375f0df7bc5d00890df43f743ad953f2674e54bfce9566115641417f82d403ff5642fdabf497f05e00b1e6b13b15e0f02b8a598455294eb75d655901b786f987680cae98260702adc06500ad6ad1b49663fd803a310bdea94fff8229c22060c40d6823f050baef83e3e61f78007309f745531c9ad99c7b9f9fe041c1d184c580b58ed5cd728adcf4837b6a82d084009375af1ee9a11c5db3da73ce8ec276711348114f6d19163e6b52c37825c33d979000ad5d8113481ddb4c7aa481b835aaae649ebc96d67996a9337447697183cc62149300e78ca9a054b151c8a0b131bb3e939e30199ac470e203f9e1bf808fd56f70253a94005f4da3f8f8f0374fae31ed80b2578f650d7e086363320c75fd61fdc1f160506a9700dd4c8aec0a4fc59f700e025f46c3f34a3a33c21129acf6204c176ddf675cc50d9d00d9e73751b542ab74c1133c6e088f01ae2eb0a50220a71ca031abe2b3907f26f79f006c7cfbc2ce48f82099a2aa20f24e4042229d680b16c758dd1c13b93683ec12ebaf0014a4115c7583af3bedfe0ce175e2f6f3397245cc504395880281fe2f6cf8ffccb0002d119c4c7f5df86abd27bcb6c8c8df59472b29f10998cf218ec4154dd88860beb4007712bc1e8849c4ab9c35b2318e343300fb2a99bc61ebd4fd6d92dc015ba6f31fc2008c1c60be05141f92bfeee3248f795f28e2acecaf56647c1431e5ced19fb31660c600f9cdb7cd33095d2b2214b0c36899b64117020647956569539312fc64d4065c87cb003e48890fb892d90bf93a24e0ea0a31009de583dfb36967d8835d36664149dedcd70036b0e024421d549a488abf7a9b037551eb0abe4540cbb511ced645917b9e7494df00775b78815785d04dade5d176e254d388b43364a9d55415d729456e0c62a016dde7001a779ff7aef2aa185ee689bbd9a35a705918613bb82b097c1da88d9cd9bcd2fff20056fb81de43335796fc3e0bc88d994dc2406ff8629e0471af40ef732e1b1f2560f60049266acf8839c7510626367ae6c392b022ee3174b439e4a9d0a2dd7851efbb84ff005878c5f671a0892e62413e48a48e237604721f6e8f6870baeaaa06c002436a8c0006100000000001a3df3924154642743df7c89de30c3fbfcc2e7ccdb487e0693929743d613a6cb9020128db81db5621fdc570cc8593aababa461ed83022ac4ee5b5fd83143d99850a1109018a849a8b1ac084049b6de037f9ede90f7e6fcc9796422d28a883ed18e05b7ac70a00f81b23926e2d382e007c479b17e69957fcb3b8a73ec7a788639842dca0abcb3b0b008a016711ddca3eb1ced4444adad1ad6f35db320391975e4e8a84a4a87c8771530d0099151e39326a7661d8ac045a627c821811c63737a1debdc07eeca10c66fd27190e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f016b6764c3b069c008262b9ce74a646d5eede153788bad31653db0ad3113e18e3b1001bbbc076ba1e3d1805ff8c0fd75ebf6e6fde7c661c61d9a44eefb562611ca5c4e1101afafe9d12e30e14ad5fd86304491349176f318c5030e6155a893b0e0e8cf63451201a45a7290887763d97fcd47cf082a8b600bade1bd985cdb1a4e7e6f9c3f2e8a9813013b990454904bc08790426c0ff1b32284c8f9f2a3079735f2cc7b2c2aaa7741b91401b5ff2d6942b82c900d243d810b8464374488ae76f5e82549686e9179fdcce60c1501cc236f5394ce85c69e0010fbc6b0585a46226dd8e8135ae15419ba630db445551601dfa3660910818c6e2d27c0c2dd0bfc1fde32a13bc23aa04c8039c3e49dc85ec0170109ecc9f38fcff21f7ac414e7a0e48a81141d642f4498621aab3b816703e47ef1", + "holds": [ + { + "time": 1718092399578, + "amount": "1000000000000", + "proof": "01000000160042848ec03c268eb7b69fe729a834db06dc3b182055fb00e5d4a066cfc1118298da834906900100000006000000050010a5d4e8080300000000da0600000007009c05d308ffbb5c59f120419b9b4ef1277f92b835c8b17262a18d51c61fbf756b460043dd210f11337ffb1290e171db7d06697ed944a7d3adf00bcb5d003b4ccadc9e6b00e7b2b80cb816e6576d998902739e2bd55a9bd1d575c189ed5dfc1336c581d64c9000c002bf557c16b76a64de47e4895424f55a53f05119336df0835591bbcb4b7c24b00094b7d84caa17d084746b0a35ff9e80d9e0b1aef6a32d7da883b3f7f378c20bc5b80034af2ffdbf7cfb287b47271b16ace78311e11966e77561bff4fb435db314dcd901210000000042848ec03c268eb7b69fe729a834db06dc3b182055fb00e5d4a066cfc11182980016100000000001a3df3924154642743df7c89de30c3fbfcc2e7ccdb487e0693929743d613a6cb9020128db81db5621fdc570cc8593aababa461ed83022ac4ee5b5fd83143d99850a11060132e10c19cbe5ea8b7ebf42b91833e92d5ec81605a5bff5ef7990eddf7e68746e09018a849a8b1ac084049b6de037f9ede90f7e6fcc9796422d28a883ed18e05b7ac70a00f81b23926e2d382e007c479b17e69957fcb3b8a73ec7a788639842dca0abcb3b0b008a016711ddca3eb1ced4444adad1ad6f35db320391975e4e8a84a4a87c8771530d0099151e39326a7661d8ac045a627c821811c63737a1debdc07eeca10c66fd27190e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f016b6764c3b069c008262b9ce74a646d5eede153788bad31653db0ad3113e18e3b1001bbbc076ba1e3d1805ff8c0fd75ebf6e6fde7c661c61d9a44eefb562611ca5c4e1101afafe9d12e30e14ad5fd86304491349176f318c5030e6155a893b0e0e8cf63451201a45a7290887763d97fcd47cf082a8b600bade1bd985cdb1a4e7e6f9c3f2e8a9813013b990454904bc08790426c0ff1b32284c8f9f2a3079735f2cc7b2c2aaa7741b91401b5ff2d6942b82c900d243d810b8464374488ae76f5e82549686e9179fdcce60c1501cc236f5394ce85c69e0010fbc6b0585a46226dd8e8135ae15419ba630db44555170109ecc9f38fcff21f7ac414e7a0e48a81141d642f4498621aab3b816703e47ef1" + }, + { + "time": 1718092584368, + "amount": "1000000000000", + "proof": "01000000160042848ec03c268eb7b69fe729a834db06dc3b182055fb00e5d4a066cfc1118298b0554c06900100000006000000050010a5d4e8080300000000b00600000007009c05d308ffbb5c59f120419b9b4ef1277f92b835c8b17262a18d51c61fbf756b460043dd210f11337ffb1290e171db7d06697ed944a7d3adf00bcb5d003b4ccadc9e6b00e7b2b80cb816e6576d998902739e2bd55a9bd1d575c189ed5dfc1336c581d64c9000c002bf557c16b76a64de47e4895424f55a53f05119336df0835591bbcb4b7c24b80034af2ffdbf7cfb287b47271b16ace78311e11966e77561bff4fb435db314dcd9da0094a48357dce8f67e158331f999e6480792bae25325054617e9938c2e55ee7f1901210000000042848ec03c268eb7b69fe729a834db06dc3b182055fb00e5d4a066cfc11182980016100000000001a3df3924154642743df7c89de30c3fbfcc2e7ccdb487e0693929743d613a6cb9020128db81db5621fdc570cc8593aababa461ed83022ac4ee5b5fd83143d99850a11060132e10c19cbe5ea8b7ebf42b91833e92d5ec81605a5bff5ef7990eddf7e68746e09018a849a8b1ac084049b6de037f9ede90f7e6fcc9796422d28a883ed18e05b7ac70a00f81b23926e2d382e007c479b17e69957fcb3b8a73ec7a788639842dca0abcb3b0b008a016711ddca3eb1ced4444adad1ad6f35db320391975e4e8a84a4a87c8771530d0099151e39326a7661d8ac045a627c821811c63737a1debdc07eeca10c66fd27190e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f016b6764c3b069c008262b9ce74a646d5eede153788bad31653db0ad3113e18e3b1001bbbc076ba1e3d1805ff8c0fd75ebf6e6fde7c661c61d9a44eefb562611ca5c4e1101afafe9d12e30e14ad5fd86304491349176f318c5030e6155a893b0e0e8cf63451201a45a7290887763d97fcd47cf082a8b600bade1bd985cdb1a4e7e6f9c3f2e8a9813013b990454904bc08790426c0ff1b32284c8f9f2a3079735f2cc7b2c2aaa7741b91401b5ff2d6942b82c900d243d810b8464374488ae76f5e82549686e9179fdcce60c1501cc236f5394ce85c69e0010fbc6b0585a46226dd8e8135ae15419ba630db44555170109ecc9f38fcff21f7ac414e7a0e48a81141d642f4498621aab3b816703e47ef1" + }, + { + "time": 1718110526059, + "amount": "10000", + "proof": "01000000160042848ec03c268eb7b69fe729a834db06dc3b182055fb00e5d4a066cfc11182986b1a5e079001000000030000000210270803000000006b0600000007009c05d308ffbb5c59f120419b9b4ef1277f92b835c8b17262a18d51c61fbf756b460043dd210f11337ffb1290e171db7d06697ed944a7d3adf00bcb5d003b4ccadc9e9000c002bf557c16b76a64de47e4895424f55a53f05119336df0835591bbcb4b7c24b00094b7d84caa17d084746b0a35ff9e80d9e0b1aef6a32d7da883b3f7f378c20bc5b80034af2ffdbf7cfb287b47271b16ace78311e11966e77561bff4fb435db314dcd9da0094a48357dce8f67e158331f999e6480792bae25325054617e9938c2e55ee7f1901210000000042848ec03c268eb7b69fe729a834db06dc3b182055fb00e5d4a066cfc11182980016100000000001a3df3924154642743df7c89de30c3fbfcc2e7ccdb487e0693929743d613a6cb9020128db81db5621fdc570cc8593aababa461ed83022ac4ee5b5fd83143d99850a11060132e10c19cbe5ea8b7ebf42b91833e92d5ec81605a5bff5ef7990eddf7e68746e09018a849a8b1ac084049b6de037f9ede90f7e6fcc9796422d28a883ed18e05b7ac70a00f81b23926e2d382e007c479b17e69957fcb3b8a73ec7a788639842dca0abcb3b0b008a016711ddca3eb1ced4444adad1ad6f35db320391975e4e8a84a4a87c8771530d0099151e39326a7661d8ac045a627c821811c63737a1debdc07eeca10c66fd27190e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f016b6764c3b069c008262b9ce74a646d5eede153788bad31653db0ad3113e18e3b1001bbbc076ba1e3d1805ff8c0fd75ebf6e6fde7c661c61d9a44eefb562611ca5c4e1101afafe9d12e30e14ad5fd86304491349176f318c5030e6155a893b0e0e8cf63451201a45a7290887763d97fcd47cf082a8b600bade1bd985cdb1a4e7e6f9c3f2e8a9813013b990454904bc08790426c0ff1b32284c8f9f2a3079735f2cc7b2c2aaa7741b91401b5ff2d6942b82c900d243d810b8464374488ae76f5e82549686e9179fdcce60c1501cc236f5394ce85c69e0010fbc6b0585a46226dd8e8135ae15419ba630db44555170109ecc9f38fcff21f7ac414e7a0e48a81141d642f4498621aab3b816703e47ef1" + }, + { + "time": 1718110593680, + "amount": "10000", + "proof": "01000000160042848ec03c268eb7b69fe729a834db06dc3b182055fb00e5d4a066cfc111829890225f07900100000003000000021027080300000000900600000007009c05d308ffbb5c59f120419b9b4ef1277f92b835c8b17262a18d51c61fbf756b460043dd210f11337ffb1290e171db7d06697ed944a7d3adf00bcb5d003b4ccadc9e6b00e7b2b80cb816e6576d998902739e2bd55a9bd1d575c189ed5dfc1336c581d64cb00094b7d84caa17d084746b0a35ff9e80d9e0b1aef6a32d7da883b3f7f378c20bc5b80034af2ffdbf7cfb287b47271b16ace78311e11966e77561bff4fb435db314dcd9da0094a48357dce8f67e158331f999e6480792bae25325054617e9938c2e55ee7f1901210000000042848ec03c268eb7b69fe729a834db06dc3b182055fb00e5d4a066cfc11182980016100000000001a3df3924154642743df7c89de30c3fbfcc2e7ccdb487e0693929743d613a6cb9020128db81db5621fdc570cc8593aababa461ed83022ac4ee5b5fd83143d99850a11060132e10c19cbe5ea8b7ebf42b91833e92d5ec81605a5bff5ef7990eddf7e68746e09018a849a8b1ac084049b6de037f9ede90f7e6fcc9796422d28a883ed18e05b7ac70a00f81b23926e2d382e007c479b17e69957fcb3b8a73ec7a788639842dca0abcb3b0b008a016711ddca3eb1ced4444adad1ad6f35db320391975e4e8a84a4a87c8771530d0099151e39326a7661d8ac045a627c821811c63737a1debdc07eeca10c66fd27190e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f016b6764c3b069c008262b9ce74a646d5eede153788bad31653db0ad3113e18e3b1001bbbc076ba1e3d1805ff8c0fd75ebf6e6fde7c661c61d9a44eefb562611ca5c4e1101afafe9d12e30e14ad5fd86304491349176f318c5030e6155a893b0e0e8cf63451201a45a7290887763d97fcd47cf082a8b600bade1bd985cdb1a4e7e6f9c3f2e8a9813013b990454904bc08790426c0ff1b32284c8f9f2a3079735f2cc7b2c2aaa7741b91401b5ff2d6942b82c900d243d810b8464374488ae76f5e82549686e9179fdcce60c1501cc236f5394ce85c69e0010fbc6b0585a46226dd8e8135ae15419ba630db44555170109ecc9f38fcff21f7ac414e7a0e48a81141d642f4498621aab3b816703e47ef1" + }, + { + "time": 1718110630663, + "amount": "10000", + "proof": "01000000160042848ec03c268eb7b69fe729a834db06dc3b182055fb00e5d4a066cfc111829807b35f079001000000030000000210270803000000000706000000460043dd210f11337ffb1290e171db7d06697ed944a7d3adf00bcb5d003b4ccadc9e6b00e7b2b80cb816e6576d998902739e2bd55a9bd1d575c189ed5dfc1336c581d64c9000c002bf557c16b76a64de47e4895424f55a53f05119336df0835591bbcb4b7c24b00094b7d84caa17d084746b0a35ff9e80d9e0b1aef6a32d7da883b3f7f378c20bc5b80034af2ffdbf7cfb287b47271b16ace78311e11966e77561bff4fb435db314dcd9da0094a48357dce8f67e158331f999e6480792bae25325054617e9938c2e55ee7f1901210000000042848ec03c268eb7b69fe729a834db06dc3b182055fb00e5d4a066cfc11182980016100000000001a3df3924154642743df7c89de30c3fbfcc2e7ccdb487e0693929743d613a6cb9020128db81db5621fdc570cc8593aababa461ed83022ac4ee5b5fd83143d99850a11060132e10c19cbe5ea8b7ebf42b91833e92d5ec81605a5bff5ef7990eddf7e68746e09018a849a8b1ac084049b6de037f9ede90f7e6fcc9796422d28a883ed18e05b7ac70a00f81b23926e2d382e007c479b17e69957fcb3b8a73ec7a788639842dca0abcb3b0b008a016711ddca3eb1ced4444adad1ad6f35db320391975e4e8a84a4a87c8771530d0099151e39326a7661d8ac045a627c821811c63737a1debdc07eeca10c66fd27190e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f016b6764c3b069c008262b9ce74a646d5eede153788bad31653db0ad3113e18e3b1001bbbc076ba1e3d1805ff8c0fd75ebf6e6fde7c661c61d9a44eefb562611ca5c4e1101afafe9d12e30e14ad5fd86304491349176f318c5030e6155a893b0e0e8cf63451201a45a7290887763d97fcd47cf082a8b600bade1bd985cdb1a4e7e6f9c3f2e8a9813013b990454904bc08790426c0ff1b32284c8f9f2a3079735f2cc7b2c2aaa7741b91401b5ff2d6942b82c900d243d810b8464374488ae76f5e82549686e9179fdcce60c1501cc236f5394ce85c69e0010fbc6b0585a46226dd8e8135ae15419ba630db44555170109ecc9f38fcff21f7ac414e7a0e48a81141d642f4498621aab3b816703e47ef1" + }, + { + "time": 1718114549574, + "amount": "10000", + "proof": "01000000160042848ec03c268eb7b69fe729a834db06dc3b182055fb00e5d4a066cfc1118298467f9b07900100000003000000021027080300000000460600000007009c05d308ffbb5c59f120419b9b4ef1277f92b835c8b17262a18d51c61fbf756b6b00e7b2b80cb816e6576d998902739e2bd55a9bd1d575c189ed5dfc1336c581d64c9000c002bf557c16b76a64de47e4895424f55a53f05119336df0835591bbcb4b7c24b00094b7d84caa17d084746b0a35ff9e80d9e0b1aef6a32d7da883b3f7f378c20bc5b80034af2ffdbf7cfb287b47271b16ace78311e11966e77561bff4fb435db314dcd9da0094a48357dce8f67e158331f999e6480792bae25325054617e9938c2e55ee7f1901210000000042848ec03c268eb7b69fe729a834db06dc3b182055fb00e5d4a066cfc11182980016100000000001a3df3924154642743df7c89de30c3fbfcc2e7ccdb487e0693929743d613a6cb9020128db81db5621fdc570cc8593aababa461ed83022ac4ee5b5fd83143d99850a11060132e10c19cbe5ea8b7ebf42b91833e92d5ec81605a5bff5ef7990eddf7e68746e09018a849a8b1ac084049b6de037f9ede90f7e6fcc9796422d28a883ed18e05b7ac70a00f81b23926e2d382e007c479b17e69957fcb3b8a73ec7a788639842dca0abcb3b0b008a016711ddca3eb1ced4444adad1ad6f35db320391975e4e8a84a4a87c8771530d0099151e39326a7661d8ac045a627c821811c63737a1debdc07eeca10c66fd27190e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f016b6764c3b069c008262b9ce74a646d5eede153788bad31653db0ad3113e18e3b1001bbbc076ba1e3d1805ff8c0fd75ebf6e6fde7c661c61d9a44eefb562611ca5c4e1101afafe9d12e30e14ad5fd86304491349176f318c5030e6155a893b0e0e8cf63451201a45a7290887763d97fcd47cf082a8b600bade1bd985cdb1a4e7e6f9c3f2e8a9813013b990454904bc08790426c0ff1b32284c8f9f2a3079735f2cc7b2c2aaa7741b91401b5ff2d6942b82c900d243d810b8464374488ae76f5e82549686e9179fdcce60c1501cc236f5394ce85c69e0010fbc6b0585a46226dd8e8135ae15419ba630db44555170109ecc9f38fcff21f7ac414e7a0e48a81141d642f4498621aab3b816703e47ef1" + }, + { + "time": 1718124588984, + "amount": "10000", + "proof": "01000000160042848ec03c268eb7b69fe729a834db06dc3b182055fb00e5d4a066cfc1118298b8af3408900100000003000000021027080300000000b80600000007009c05d308ffbb5c59f120419b9b4ef1277f92b835c8b17262a18d51c61fbf756b460043dd210f11337ffb1290e171db7d06697ed944a7d3adf00bcb5d003b4ccadc9e6b00e7b2b80cb816e6576d998902739e2bd55a9bd1d575c189ed5dfc1336c581d64c9000c002bf557c16b76a64de47e4895424f55a53f05119336df0835591bbcb4b7c24b00094b7d84caa17d084746b0a35ff9e80d9e0b1aef6a32d7da883b3f7f378c20bc5da0094a48357dce8f67e158331f999e6480792bae25325054617e9938c2e55ee7f1901210000000042848ec03c268eb7b69fe729a834db06dc3b182055fb00e5d4a066cfc11182980016100000000001a3df3924154642743df7c89de30c3fbfcc2e7ccdb487e0693929743d613a6cb9020128db81db5621fdc570cc8593aababa461ed83022ac4ee5b5fd83143d99850a11060132e10c19cbe5ea8b7ebf42b91833e92d5ec81605a5bff5ef7990eddf7e68746e09018a849a8b1ac084049b6de037f9ede90f7e6fcc9796422d28a883ed18e05b7ac70a00f81b23926e2d382e007c479b17e69957fcb3b8a73ec7a788639842dca0abcb3b0b008a016711ddca3eb1ced4444adad1ad6f35db320391975e4e8a84a4a87c8771530d0099151e39326a7661d8ac045a627c821811c63737a1debdc07eeca10c66fd27190e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f016b6764c3b069c008262b9ce74a646d5eede153788bad31653db0ad3113e18e3b1001bbbc076ba1e3d1805ff8c0fd75ebf6e6fde7c661c61d9a44eefb562611ca5c4e1101afafe9d12e30e14ad5fd86304491349176f318c5030e6155a893b0e0e8cf63451201a45a7290887763d97fcd47cf082a8b600bade1bd985cdb1a4e7e6f9c3f2e8a9813013b990454904bc08790426c0ff1b32284c8f9f2a3079735f2cc7b2c2aaa7741b91401b5ff2d6942b82c900d243d810b8464374488ae76f5e82549686e9179fdcce60c1501cc236f5394ce85c69e0010fbc6b0585a46226dd8e8135ae15419ba630db44555170109ecc9f38fcff21f7ac414e7a0e48a81141d642f4498621aab3b816703e47ef1" + } + ] +} From 93b5c2dce0dd89c01a8cf3ef392f9593424c8d8a Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 14 Jun 2024 14:29:00 +0200 Subject: [PATCH 034/126] fix BidAddrKey parsing Signed-off-by: David Hernando --- Casper.Network.SDK.Test/GlobalStateKeyTest.cs | 56 ++++++++++++++++++ .../Types/GlobalStateKey/BidAddrKey.cs | 57 ++++++++++++------- .../Types/GlobalStateKey/GlobalStateKey.cs | 4 ++ 3 files changed, 97 insertions(+), 20 deletions(-) diff --git a/Casper.Network.SDK.Test/GlobalStateKeyTest.cs b/Casper.Network.SDK.Test/GlobalStateKeyTest.cs index 2e99f18..f557889 100644 --- a/Casper.Network.SDK.Test/GlobalStateKeyTest.cs +++ b/Casper.Network.SDK.Test/GlobalStateKeyTest.cs @@ -608,5 +608,61 @@ public void MessageIndexKeyTest() Assert.AreEqual(msgKeyStr, messageKey.ToString().ToLower()); Assert.AreEqual(msgKeyStr, key.ToString().ToLower()); } + + [Test] + public void BidAddrKeyTest() + { + { + var unifiedBidAddrKeyStr = "bid-addr-002f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c458007309"; + var key = GlobalStateKey.FromString(unifiedBidAddrKeyStr); + Assert.IsNotNull(key); + Assert.AreEqual(KeyIdentifier.BidAddr, key.KeyIdentifier); + + var bidAddrKey = key as BidAddrKey; + Assert.AreEqual(BidAddrTag.Unified, bidAddrKey.Tag); + Assert.AreEqual("account-hash-2f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c458007309", bidAddrKey.Unified.ToString().ToLower()); + Assert.IsNull(bidAddrKey.Delegator); + Assert.AreEqual(0, bidAddrKey.EraId); + + } + { + var validatorBidAddrKeyStr = "bid-addr-012f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c458007309"; + var key = GlobalStateKey.FromString(validatorBidAddrKeyStr); + Assert.IsNotNull(key); + Assert.AreEqual(KeyIdentifier.BidAddr, key.KeyIdentifier); + + var bidAddrKey = key as BidAddrKey; + Assert.AreEqual(BidAddrTag.Validator, bidAddrKey.Tag); + Assert.AreEqual("account-hash-2f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c458007309", bidAddrKey.Validator.ToString().ToLower()); + Assert.IsNull(bidAddrKey.Delegator); + Assert.AreEqual(0, bidAddrKey.EraId); + } + { + var delegatorBidAddrKeyStr = "bid-addr-022f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c4580073099fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4"; + var key = GlobalStateKey.FromString(delegatorBidAddrKeyStr); + Assert.IsNotNull(key); + Assert.AreEqual(KeyIdentifier.BidAddr, key.KeyIdentifier); + + var bidAddrKey = key as BidAddrKey; + Assert.AreEqual(BidAddrTag.Delegator, bidAddrKey.Tag); + Assert.AreEqual("account-hash-2f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c458007309", bidAddrKey.Validator.ToString().ToLower()); + Assert.AreEqual("account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4", bidAddrKey.Delegator.ToString().ToLower()); + Assert.AreEqual(0, bidAddrKey.EraId); + + } + { + var creditBidAddrKeyStr = + "bid-addr-04520037cd249ccbcfeb0b9feae07d8d4f7d922cf88adc4f3e8691f9d34ccc8d097f00000000000000"; + var key = GlobalStateKey.FromString(creditBidAddrKeyStr); + Assert.IsNotNull(key); + Assert.AreEqual(KeyIdentifier.BidAddr, key.KeyIdentifier); + + var bidAddrKey = key as BidAddrKey; + Assert.AreEqual(BidAddrTag.Credit, bidAddrKey.Tag); + Assert.AreEqual("account-hash-520037cd249ccbcfeb0b9feae07d8d4f7d922cf88adc4f3e8691f9d34ccc8d09", bidAddrKey.Validator.ToString().ToLower()); + Assert.IsNull(bidAddrKey.Delegator); + Assert.AreEqual(127, bidAddrKey.EraId); + } + } } } diff --git a/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs index 0ab3d81..697aaa1 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs @@ -18,6 +18,10 @@ public enum BidAddrTag /// Delegator BidAddr, /// Delegator = 2, + /// + /// BidAddr for auction credit. + /// + Credit = 4, } public class BidAddrKey : GlobalStateKey @@ -26,48 +30,61 @@ public class BidAddrKey : GlobalStateKey public BidAddrTag Tag { get; init; } - public AccountHashKey Validator - { - get - { - var hash = Tag == BidAddrTag.Delegator ? Key.Substring(0,32) : Key; - return new AccountHashKey("account-hash-" + hash); - } - } + /// + /// Unified BidAddr. + /// + public AccountHashKey Unified { get; init; } - public AccountHashKey Delegator - { - get - { - var hash = Tag == BidAddrTag.Delegator ? Key.Substring(32) : null; - return hash != null - ? new AccountHashKey("account-hash-" + hash) - : null; - } - } + /// + /// The valicator address. + /// + public AccountHashKey Validator { get; init; } + + /// + /// The delegator address. + /// + public AccountHashKey Delegator { get; init; } + + /// + /// The era id. + /// + public ulong EraId { get; init; } public BidAddrKey(string key) : base(key, KEYPREFIX) { KeyIdentifier = KeyIdentifier.BidAddr; - var bytes = Hex.Decode(Key); - if (bytes.Length <= 0) + var bytes = Hex.Decode(key.Substring(key.LastIndexOf('-') + 1)); + + if (bytes.Length == 0) throw new Exception("Wrong key length."); + switch (bytes[0]) { case (byte)BidAddrTag.Unified: Tag = BidAddrTag.Unified; if (bytes.Length != 33) throw new Exception("Wrong key length for Unified BidAddr. Expected 33 bytes."); + Unified = new AccountHashKey(bytes.Slice(1, 33)); break; case (byte)BidAddrTag.Validator: Tag = BidAddrTag.Validator; if (bytes.Length != 33) throw new Exception("Wrong key length for Validator BidAddr. Expected 33 bytes."); + Validator = new AccountHashKey(bytes.Slice(1, 33)); break; case (byte)BidAddrTag.Delegator: Tag = BidAddrTag.Delegator; if (bytes.Length != 65) throw new Exception("Wrong key length for Unified BidAddr. Expected 65 bytes."); + Validator = new AccountHashKey(bytes.Slice(1, 33)); + Delegator = new AccountHashKey(bytes.Slice(33)); + break; + case (byte)BidAddrTag.Credit: + Tag = BidAddrTag.Credit; + if (bytes.Length != 41) + throw new Exception("Wrong key length for Credit BidAddr. Expected 41 bytes."); + Validator = new AccountHashKey(bytes.Slice(1, 33)); + EraId = BitConverterExtensions.ToUInt64(bytes.Slice(33)); break; default: throw new Exception($"Wrong BidAddr tag '{bytes[0]}'."); diff --git a/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs index cc7f62e..ba27a07 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs @@ -356,6 +356,10 @@ public AccountHashKey(PublicKey publicKey) : base(publicKey.GetAccountHash(), KEYPREFIX) { } + + public AccountHashKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + { + } } /// From da8bd04df763da07ccbd92cc2b4a3ccb0dd3eefb Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 20 Jun 2024 10:38:28 +0200 Subject: [PATCH 035/126] fix AddressableEntityKey serialization to bytes Signed-off-by: David Hernando --- .../GlobalStateKey/AddressableEntityKey.cs | 90 ++++++++++++------- 1 file changed, 59 insertions(+), 31 deletions(-) diff --git a/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs index 063101d..f9aacdf 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs @@ -1,36 +1,37 @@ using System; +using System.Collections.Generic; using System.IO; using Casper.Network.SDK.Utils; using Org.BouncyCastle.Utilities.Encoders; namespace Casper.Network.SDK.Types { - public enum EntityKind { + public enum EntityKindEnum { /// - /// System variant. + /// Package associated with a native contract implementation. /// System = 0, /// - /// Account variant. + /// Package associated with an Account hash. /// Account = 1, /// - /// SmartContract variant. + /// Package associated with Wasm stored on chain. /// Contract = 2, } - public static class EntityKindExtensions + public static class EntityKinEnumExtensions { - public static string ToKeyPrefix(this EntityKind kind) + public static string ToKeyPrefix(this EntityKindEnum kind) { switch (kind) { - case EntityKind.System: + case EntityKindEnum.System: return "entity-system-"; - case EntityKind.Account: + case EntityKindEnum.Account: return "entity-account-"; - case EntityKind.Contract: + case EntityKindEnum.Contract: return "entity-contract-"; default: return kind.ToString(); @@ -38,18 +39,18 @@ public static string ToKeyPrefix(this EntityKind kind) } } - public class AddressableEntityKey : GlobalStateKey + public class AddressableEntityKey : GlobalStateKey, IEntityIdentifier { - public EntityKind Kind { get; init; } + public EntityKindEnum Kind { get; init; } private static string GetPrefix(string key) { - if (key.StartsWith(EntityKind.System.ToKeyPrefix())) - return EntityKind.System.ToKeyPrefix(); - if (key.StartsWith(EntityKind.Account.ToKeyPrefix())) - return EntityKind.Account.ToKeyPrefix(); - if (key.StartsWith(EntityKind.Contract.ToKeyPrefix())) - return EntityKind.Contract.ToKeyPrefix(); + if (key.StartsWith(EntityKindEnum.System.ToKeyPrefix())) + return EntityKindEnum.System.ToKeyPrefix(); + if (key.StartsWith(EntityKindEnum.Account.ToKeyPrefix())) + return EntityKindEnum.Account.ToKeyPrefix(); + if (key.StartsWith(EntityKindEnum.Contract.ToKeyPrefix())) + return EntityKindEnum.Contract.ToKeyPrefix(); throw new Exception("Unexpected key prefix in NamedKeyKey: " + key); @@ -58,21 +59,21 @@ public AddressableEntityKey(string key) : base(key, GetPrefix(key)) { KeyIdentifier = KeyIdentifier.AddressableEntity; var prefix = GetPrefix(key); - if (EntityKind.System.ToKeyPrefix().Equals(prefix)) - Kind = EntityKind.System; - else if (EntityKind.Account.ToKeyPrefix().Equals(prefix)) - Kind = EntityKind.Account; - else if (EntityKind.Contract.ToKeyPrefix().Equals(prefix)) - Kind = EntityKind.Contract; + if (EntityKindEnum.System.ToKeyPrefix().Equals(prefix)) + Kind = EntityKindEnum.System; + else if (EntityKindEnum.Account.ToKeyPrefix().Equals(prefix)) + Kind = EntityKindEnum.Account; + else if (EntityKindEnum.Contract.ToKeyPrefix().Equals(prefix)) + Kind = EntityKindEnum.Contract; } public AddressableEntityKey(byte[] key) : this( - key[0] == (byte)EntityKind.System - ? EntityKind.System.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) - : (key[0] == (byte)EntityKind.Account - ? EntityKind.Account.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) - : (key[0] == (byte)EntityKind.Account - ? EntityKind.Contract.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + key[0] == (byte)EntityKindEnum.System + ? EntityKindEnum.System.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + : (key[0] == (byte)EntityKindEnum.Account + ? EntityKindEnum.Account.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + : (key[0] == (byte)EntityKindEnum.Account + ? EntityKindEnum.Contract.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) : throw new Exception($"Wrong entity tag '{key[0]}' for AddressableEntityKey.")))) { } @@ -82,8 +83,35 @@ public AddressableEntityKey(BinaryReader reader) : base(null) KeyIdentifier = KeyIdentifier.AddressableEntity; var tag = reader.ReadByte(); var addr = reader.ReadBytes(32); - Kind = (EntityKind)tag; + Kind = (EntityKindEnum)tag; Key = Kind.ToKeyPrefix() + Hex.ToHexString(addr); } + + protected override byte[] _GetRawBytesFromKey(string key) + { + return this.GetBytes(); + } + + public override byte[] GetBytes() + { + var key = Key.Substring(Key.LastIndexOf('-')+1); + var rawBytes = Hex.Decode(key); + var ms = new MemoryStream(); + ms.WriteByte((byte)this.Kind); + ms.Write(rawBytes); + + return ms.ToArray(); + } + + /// + /// Returns an EntityIdentifier object as defined in the RPC schema for an account hash key. + /// + public Dictionary GetEntityIdentifier() + { + return new Dictionary + { + {"EntityAddr", Key} + }; + } } -} \ No newline at end of file +} From d4c97d4292c61da11d41be04b26ae05237152bbd Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 20 Jun 2024 11:52:59 +0200 Subject: [PATCH 036/126] added BlockTransaction type to expose a single list of transactions instead of 6 lists Signed-off-by: David Hernando --- Casper.Network.SDK/Types/Block.cs | 131 +++++++++++++++++++++++------- 1 file changed, 100 insertions(+), 31 deletions(-) diff --git a/Casper.Network.SDK/Types/Block.cs b/Casper.Network.SDK/Types/Block.cs index c94ff6a..57dc05c 100644 --- a/Casper.Network.SDK/Types/Block.cs +++ b/Casper.Network.SDK/Types/Block.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; -using Org.BouncyCastle.Asn1.X509.Qualified; namespace Casper.Network.SDK.Types { @@ -187,35 +186,91 @@ public class BlockBodyV1 public List TransferHashes { get; init; } } + public enum TransactionCategory { + /// Native mint interaction (the default). + Mint = 0, + /// Native auction interaction. + Auction = 1, + /// Install or Upgrade. + InstallUpgrade = 2, + /// A large Wasm based transaction. + Large = 3, + /// A medium Wasm based transaction. + Medium = 4, + /// A small Wasm based transaction. + Small = 5, + } + + public enum TransactionVersion + { + Deploy = 0, + Version1 = 1, + } + + public class BlockTransaction + { + public TransactionCategory Category { get; init; } + public TransactionVersion Version { get; init; } + public string Hash { get; init; } + + public class BlockTransactionConverter : JsonConverter> + { + public override List Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + var txs = new List(); + + try + { + reader.Read(); // skip start object + while (reader.TokenType == JsonTokenType.PropertyName) + { + var categoryNumber = reader.GetString(); + var category = EnumCompat.Parse(categoryNumber); + reader.Read(); + var list = JsonSerializer.Deserialize>(ref reader, options); + reader.Read(); + if (list.Count > 0) + { + txs.AddRange(list.Select(t => new BlockTransaction() + { + Category = category, + Hash = t.Deploy != null ? t.Deploy : t.Version1, + Version = t.Deploy != null ? TransactionVersion.Deploy : TransactionVersion.Version1, + })); + } + } + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + + return txs; + } + + public override void Write( + Utf8JsonWriter writer, + List block, + JsonSerializerOptions options) + { + throw new JsonException($"Serialization of BlockTransaction not available"); + } + } + } + /// /// A block body /// public class BlockBody { - /// - /// The hashes of the installer/upgrader transactions within the block. - /// - [JsonPropertyName("install_upgrade")] - public List InstallUpgrade { get; init; } - - /// - /// The hashes of the auction transactions within the block. - /// - [JsonPropertyName("auction")] - public List Auction { get; init; } - - /// - /// The hashes of all other (non-installer/upgrader) transactions within the block. - /// - [JsonPropertyName("standard")] - public List Standard { get; init; } - - /// - /// The hashes of the mint transactions within the block. - /// - [JsonPropertyName("mint")] - public List Mint { get; init; } - + [JsonPropertyName("transactions")] + [JsonConverter(typeof(BlockTransaction.BlockTransactionConverter))] + public List Transactions { get; init; } + // public Dictionary> Transactions { get; init; } + /// /// Describes finality signatures that will be rewarded in a block. Consists of a vector of /// `SingleBlockRewardedSignatures`, each of which describes signatures for a single ancestor block. @@ -410,17 +465,31 @@ internal static BlockHeader BlockHeaderFromV1(BlockHeaderV1 header, BlockBodyV1 internal static BlockBody BlockBodyFromV1(BlockBodyV1 body) { + var txs = new List(); + var mintTransactions = - body.TransferHashes.Select(t => new TransactionHash { Deploy = t }).ToList(); + body.TransferHashes.Select(hash => new BlockTransaction() + { + Category = TransactionCategory.Mint, + Hash = hash, + Version = TransactionVersion.Deploy, + }).ToList(); + if(mintTransactions.Count > 0) + txs.AddRange(mintTransactions); + var standardTransactions = - body.DeployHashes.Select(t => new TransactionHash { Deploy = t }).ToList(); + body.DeployHashes.Select(hash => new BlockTransaction() + { + Category = TransactionCategory.Large, + Hash = hash, + Version = TransactionVersion.Deploy, + }).ToList(); + if(standardTransactions.Count > 0) + txs.AddRange(standardTransactions); return new BlockBody { - Auction = new List(), - Mint = mintTransactions, - Standard = standardTransactions, - InstallUpgrade = new List(), + Transactions = txs, RewardedSignatures = null, }; } From ef948866a9e34b23d0b8b17aabac7f4c5cb4ba46 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 20 Jun 2024 12:06:40 +0200 Subject: [PATCH 037/126] updated tests Signed-off-by: David Hernando --- .../RPCResponses/GetBlockResultTest.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Casper.Network.SDK.Test/RPCResponses/GetBlockResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetBlockResultTest.cs index d216f23..676e229 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetBlockResultTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetBlockResultTest.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Linq; using Casper.Network.SDK.JsonRpc.ResultTypes; using Casper.Network.SDK.Types; using NUnit.Framework; @@ -25,6 +26,12 @@ public void GetBlockResultTest_v156() Assert.AreEqual(2947381, result.Block.Header.Height); Assert.AreEqual(13670, result.Block.Header.EraId); Assert.IsNull(result.Block.Header.EraEnd); + Assert.IsNull(result.Block.Body.RewardedSignatures); + Assert.AreEqual(26, result.Block.Body.Transactions.Count); + Assert.AreEqual(25, result.Block.Body.Transactions + .Where(t => t.Category==TransactionCategory.Large).ToList().Count); + Assert.AreEqual("31849e17f715273ad7032d51534c5b6029dd0dec1e01c225c50083752a750219", + result.Block.Body.Transactions.First(t => t.Category==TransactionCategory.Mint).Hash); } [Test] @@ -49,6 +56,9 @@ public void GetBlockResultTest_v200() Assert.IsNotNull(eraEnd.NextEraValidatorWeights[1].PublicKey); Assert.IsNotNull(eraEnd.NextEraValidatorWeights[1].Weight); Assert.IsTrue(eraEnd.Rewards.Count > 1); + Assert.AreEqual(1, result.Block.Body.Transactions.Count); + Assert.AreEqual(TransactionCategory.InstallUpgrade, result.Block.Body.Transactions[0].Category); + Assert.IsNotEmpty(result.Block.Body.Transactions[0].Hash); } } } \ No newline at end of file From dd0943968d72b0724d76e9eb9a61fdaa627d8f35 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 26 Jun 2024 09:57:17 +0200 Subject: [PATCH 038/126] Flattened Block type and added cast operator to retrieve original BlockV2 instance. Signed-off-by: David Hernando --- .../RPCResponses/GetBlockResultTest.cs | 50 +- Casper.Network.SDK/Types/Block.cs | 428 ++++++++++-------- Casper.Network.SDK/Types/BlockProof.cs | 24 + Casper.Network.SDK/Types/EraEnd.cs | 7 + 4 files changed, 314 insertions(+), 195 deletions(-) create mode 100644 Casper.Network.SDK/Types/BlockProof.cs diff --git a/Casper.Network.SDK.Test/RPCResponses/GetBlockResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetBlockResultTest.cs index 676e229..16ee124 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetBlockResultTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetBlockResultTest.cs @@ -23,15 +23,23 @@ public void GetBlockResultTest_v156() AssertExtensions.IsValidHex(result.Proofs[1].Signature.ToString(), 65); Assert.AreEqual(1, result.Block.Version); AssertExtensions.IsHash(result.Block.Hash); - Assert.AreEqual(2947381, result.Block.Header.Height); - Assert.AreEqual(13670, result.Block.Header.EraId); - Assert.IsNull(result.Block.Header.EraEnd); - Assert.IsNull(result.Block.Body.RewardedSignatures); - Assert.AreEqual(26, result.Block.Body.Transactions.Count); - Assert.AreEqual(25, result.Block.Body.Transactions + Assert.AreEqual(2947381, result.Block.Height); + Assert.AreEqual(13670, result.Block.EraId); + Assert.IsNull(result.Block.EraEnd); + Assert.IsNull(result.Block.RewardedSignatures); + Assert.AreEqual(26, result.Block.Transactions.Count); + Assert.AreEqual(25, result.Block.Transactions .Where(t => t.Category==TransactionCategory.Large).ToList().Count); Assert.AreEqual("31849e17f715273ad7032d51534c5b6029dd0dec1e01c225c50083752a750219", - result.Block.Body.Transactions.First(t => t.Category==TransactionCategory.Mint).Hash); + result.Block.Transactions.First(t => t.Category==TransactionCategory.Mint).Hash); + + var blockV1 = (BlockV1)result.Block; + AssertExtensions.IsHash(blockV1.Hash); + Assert.AreEqual(2947381, blockV1.Header.Height); + Assert.AreEqual(13670, blockV1.Header.EraId); + Assert.IsNull(blockV1.Header.EraEnd); + Assert.AreEqual(1, blockV1.Body.TransferHashes.Count); + Assert.AreEqual(25, blockV1.Body.DeployHashes.Count); } [Test] @@ -48,17 +56,31 @@ public void GetBlockResultTest_v200() AssertExtensions.IsValidHex(result.Proofs[1].Signature.ToString(), 65); Assert.AreEqual(2, result.Block.Version); AssertExtensions.IsHash(result.Block.Hash); - Assert.AreEqual(1551, result.Block.Header.Height); - Assert.AreEqual(141, result.Block.Header.EraId); - Assert.IsNotNull(result.Block.Header.EraEnd); - var eraEnd = result.Block.Header.EraEnd; + Assert.AreEqual(1551, result.Block.Height); + Assert.AreEqual(141, result.Block.EraId); + Assert.IsNotNull(result.Block.EraEnd); + var eraEnd = result.Block.EraEnd; + Assert.IsTrue(eraEnd.NextEraValidatorWeights.Count > 1); + Assert.IsNotNull(eraEnd.NextEraValidatorWeights[1].PublicKey); + Assert.IsNotNull(eraEnd.NextEraValidatorWeights[1].Weight); + Assert.IsTrue(eraEnd.Rewards.Count > 1); + Assert.AreEqual(1, result.Block.Transactions.Count); + Assert.AreEqual(TransactionCategory.InstallUpgrade, result.Block.Transactions[0].Category); + Assert.IsNotEmpty(result.Block.Transactions[0].Hash); + + var blockV2 = (BlockV2)result.Block; + AssertExtensions.IsHash(blockV2.Hash); + Assert.AreEqual(1551, blockV2.Header.Height); + Assert.AreEqual(141, blockV2.Header.EraId); + Assert.IsNotNull(blockV2.Header.EraEnd); + eraEnd = blockV2.Header.EraEnd; Assert.IsTrue(eraEnd.NextEraValidatorWeights.Count > 1); Assert.IsNotNull(eraEnd.NextEraValidatorWeights[1].PublicKey); Assert.IsNotNull(eraEnd.NextEraValidatorWeights[1].Weight); Assert.IsTrue(eraEnd.Rewards.Count > 1); - Assert.AreEqual(1, result.Block.Body.Transactions.Count); - Assert.AreEqual(TransactionCategory.InstallUpgrade, result.Block.Body.Transactions[0].Category); - Assert.IsNotEmpty(result.Block.Body.Transactions[0].Hash); + Assert.AreEqual(1, blockV2.Body.Transactions.Count); + Assert.AreEqual(TransactionCategory.InstallUpgrade, blockV2.Body.Transactions[0].Category); + Assert.IsNotEmpty(blockV2.Body.Transactions[0].Hash); } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Block.cs b/Casper.Network.SDK/Types/Block.cs index 57dc05c..be0f41a 100644 --- a/Casper.Network.SDK/Types/Block.cs +++ b/Casper.Network.SDK/Types/Block.cs @@ -73,13 +73,13 @@ public class BlockHeaderV1 public string Timestamp { get; init; } } - public class BlockHeader : BlockHeaderV1 + public class BlockHeaderV2 : BlockHeaderV1 { /// /// The `EraEnd` of a block if it is a switch block. /// [JsonPropertyName("era_end")] - public EraEndV2 EraEnd { get; init; } + public EraEnd EraEnd { get; init; } /// /// The gas price of the era. @@ -98,6 +98,13 @@ public class BlockHeader : BlockHeaderV1 public string LastSwitchBlockHash { get; init; } } + /// + /// A block header (alias for BlockHeaderV2) + /// + public class BlockHeader : BlockHeaderV2 + { + } + /// /// Validator that proposed the block /// @@ -186,6 +193,124 @@ public class BlockBodyV1 public List TransferHashes { get; init; } } + /// + /// A block body + /// + public class BlockBodyV2 + { + [JsonPropertyName("transactions")] + [JsonConverter(typeof(BlockTransaction.BlockTransactionConverter))] + public List Transactions { get; init; } + // public Dictionary> Transactions { get; init; } + + /// + /// Describes finality signatures that will be rewarded in a block. Consists of a vector of + /// `SingleBlockRewardedSignatures`, each of which describes signatures for a single ancestor block. + /// The first entry represents the signatures for the parent block, the second for the parent of the parent, + /// and so on. + /// + [JsonPropertyName("rewarded_signatures")] + public List> RewardedSignatures { get; init; } + } + + /// + /// A block in the network + /// + public class BlockV1 + { + /// + /// Block hash + /// + [JsonPropertyName("hash")] + public string Hash { get; init; } + + /// + /// Block header + /// + [JsonPropertyName("header")] + public BlockHeaderV1 Header { get; init; } + + /// + /// Block body + /// + [JsonPropertyName("body")] + public BlockBodyV1 Body { get; init; } + + internal static EraEnd EraEndFromV1(BlockHeaderV1 header, BlockBodyV1 body) + { + Dictionary> rewards = new(); + if (header.EraEnd != null && header.EraEnd.EraReport.Rewards.Count > 0) + { + foreach (var r in header.EraEnd.EraReport.Rewards) + { + rewards.Add(r.PublicKey.ToString().ToLower(), + new List { r.Amount.ToString()}); + } + } + + EraEnd eraEnd = header.EraEnd == null ? null : new EraEnd + { + NextEraValidatorWeights = header.EraEnd.NextEraValidatorWeights, + NextEraGasPrice = 1, + Equivocators = header.EraEnd.EraReport.Equivocators, + InactiveValidators = header.EraEnd.EraReport.InactiveValidators, + Rewards = rewards, + }; + return eraEnd; + } + + internal static List BlockTransactionsFromV1(BlockBodyV1 body) + { + var txs = new List(); + + var mintTransactions = + body.TransferHashes.Select(hash => new BlockTransaction() + { + Category = TransactionCategory.Mint, + Hash = hash, + Version = TransactionVersion.Deploy, + }).ToList(); + if(mintTransactions.Count > 0) + txs.AddRange(mintTransactions); + + var standardTransactions = + body.DeployHashes.Select(hash => new BlockTransaction() + { + Category = TransactionCategory.Large, + Hash = hash, + Version = TransactionVersion.Deploy, + }).ToList(); + if(standardTransactions.Count > 0) + txs.AddRange(standardTransactions); + + return txs; + } + } + + /// + /// A block in the network + /// + public class BlockV2 + { + /// + /// Block hash + /// + [JsonPropertyName("hash")] + public string Hash { get; init; } + + /// + /// Block header + /// + [JsonPropertyName("header")] + public BlockHeaderV2 Header { get; init; } + + /// + /// Block body + /// + [JsonPropertyName("body")] + public BlockBodyV2 Body { get; init; } + } + public enum TransactionCategory { /// Native mint interaction (the default). Mint = 0, @@ -260,26 +385,6 @@ public override void Write( } } } - - /// - /// A block body - /// - public class BlockBody - { - [JsonPropertyName("transactions")] - [JsonConverter(typeof(BlockTransaction.BlockTransactionConverter))] - public List Transactions { get; init; } - // public Dictionary> Transactions { get; init; } - - /// - /// Describes finality signatures that will be rewarded in a block. Consists of a vector of - /// `SingleBlockRewardedSignatures`, each of which describes signatures for a single ancestor block. - /// The first entry represents the signatures for the parent block, the second for the parent of the parent, - /// and so on. - /// - [JsonPropertyName("rewarded_signatures")] - public List> RewardedSignatures { get; init; } - } public class Block { @@ -294,45 +399,80 @@ public int Version } protected BlockV1 _blockV1; - - // Explicit cast operator from A to B - public static explicit operator BlockV1(Block block) - { - if(block._version == 1) - return block._blockV1; - throw new InvalidCastException("Version2 block cannot be converted to Version1"); - } + protected BlockV2 _blockV2; - public static explicit operator Block(BlockV1 block) - { - return new Block - { - _version = 1, - _blockV1 = block, - Hash = block.Hash, - Header = BlockV1.BlockHeaderFromV1(block.Header, block.Body), - Body = BlockV1.BlockBodyFromV1(block.Body), - }; - } - /// /// Block hash /// - [JsonPropertyName("hash")] public string Hash { get; init; } /// - /// Block header + /// A seed needed for initializing a future era. /// - [JsonPropertyName("header")] - public BlockHeader Header { get; init; } + public string AccumulatedSeed { get; init; } /// - /// Block body + /// The era ID in which this block was created. /// - [JsonPropertyName("body")] - public BlockBody Body { get; init; } + public ulong EraId { get; init; } + + /// + /// The height of this block, i.e. the number of ancestors. + /// + public ulong Height { get; init; } + + /// + /// The parent block's hash. + /// + public string ParentHash { get; init; } + + /// + /// The protocol version of the network from when this block was created. + /// + public string ProtocolVersion { get; init; } + + /// + /// A random bit needed for initializing a future era. + /// + public bool RandomBit { get; init; } + + /// + /// The root hash of global state after the deploys in this block have been executed. + /// + public string StateRootHash { get; init; } + + /// + /// The timestamp from when the block was proposed. + /// + public string Timestamp { get; init; } + + /// + /// The `EraEnd` of a block if it is a switch block. + /// + public EraEnd EraEnd { get; init; } + + /// + /// The gas price of the era. + /// + public UInt16 CurrentGasPrice { get; init; } + + /// + /// Public key of the validator that proposed the block + /// + public Proposer Proposer { get; init; } + + public string LastSwitchBlockHash { get; init; } + + public List Transactions { get; init; } + + /// + /// Describes finality signatures that will be rewarded in a block. Consists of a vector of + /// `SingleBlockRewardedSignatures`, each of which describes signatures for a single ancestor block. + /// The first entry represents the signatures for the parent block, the second for the parent of the parent, + /// and so on. + /// + public List> RewardedSignatures { get; init; } /// /// Json converter class to serialize/deserialize a Block to/from Json @@ -358,13 +498,7 @@ public override Block Read( case "Version2": var blockv2 = JsonSerializer.Deserialize(ref reader, options); reader.Read(); - return new Block - { - _version = 2, - Hash = blockv2.Hash, - Header = blockv2.Header, - Body = blockv2.Body, - }; + return (Block)blockv2; default: throw new JsonException("Expected Version1 or Version2"); } @@ -399,143 +533,75 @@ public override void Write( } } } - } - - /// - /// A block in the network - /// - public class BlockV1 - { - /// - /// Block hash - /// - [JsonPropertyName("hash")] - public string Hash { get; init; } - /// - /// Block header - /// - [JsonPropertyName("header")] - public BlockHeaderV1 Header { get; init; } + public static explicit operator BlockV1(Block block) + { + if(block._version == 1) + return block._blockV1; - /// - /// Block body - /// - [JsonPropertyName("body")] - public BlockBodyV1 Body { get; init; } + throw new InvalidCastException("Version2 block cannot be converted to Version1"); + } + + public static explicit operator BlockV2(Block block) + { + if(block._version == 2) + return block._blockV2; - internal static BlockHeader BlockHeaderFromV1(BlockHeaderV1 header, BlockBodyV1 body) + throw new InvalidCastException("Version1 block cannot be converted to Version2"); + } + + public static explicit operator Block(BlockV1 block) { - Dictionary> rewards = new(); - if (header.EraEnd != null && header.EraEnd.EraReport.Rewards.Count > 0) - { - foreach (var r in header.EraEnd.EraReport.Rewards) - { - rewards.Add(r.PublicKey.ToString().ToLower(), - new List { r.Amount.ToString()}); - } - } - - EraEndV2 eraEnd = header.EraEnd == null ? null : new EraEndV2 - { - NextEraValidatorWeights = header.EraEnd.NextEraValidatorWeights, - NextEraGasPrice = 1, - Equivocators = header.EraEnd.EraReport.Equivocators, - InactiveValidators = header.EraEnd.EraReport.InactiveValidators, - Rewards = rewards, - }; - - return new BlockHeader + return new Block { - BodyHash = header.BodyHash, - ParentHash = header.ParentHash, - Height = header.Height, - Timestamp = header.Timestamp, - EraId = header.EraId, - RandomBit = header.RandomBit, - AccumulatedSeed = header.AccumulatedSeed, - StateRootHash = header.StateRootHash, - ProtocolVersion = header.ProtocolVersion, + _version = 1, + _blockV1 = block, + + Hash = block.Hash, + + AccumulatedSeed = block.Header.AccumulatedSeed, + EraId = block.Header.EraId, + Height = block.Header.Height, + ParentHash = block.Header.ParentHash, + ProtocolVersion = block.Header.ProtocolVersion, + StateRootHash = block.Header.StateRootHash, + Timestamp = block.Header.Timestamp, + RandomBit = block.Header.RandomBit, CurrentGasPrice = 1, LastSwitchBlockHash = null, - Proposer = body.Proposer, - EraEnd = eraEnd, + Proposer = block.Body.Proposer, + EraEnd = BlockV1.EraEndFromV1(block.Header, block.Body), + + Transactions = BlockV1.BlockTransactionsFromV1(block.Body), + RewardedSignatures = null, }; } - - internal static BlockBody BlockBodyFromV1(BlockBodyV1 body) + + public static explicit operator Block(BlockV2 block) { - var txs = new List(); - - var mintTransactions = - body.TransferHashes.Select(hash => new BlockTransaction() - { - Category = TransactionCategory.Mint, - Hash = hash, - Version = TransactionVersion.Deploy, - }).ToList(); - if(mintTransactions.Count > 0) - txs.AddRange(mintTransactions); - - var standardTransactions = - body.DeployHashes.Select(hash => new BlockTransaction() - { - Category = TransactionCategory.Large, - Hash = hash, - Version = TransactionVersion.Deploy, - }).ToList(); - if(standardTransactions.Count > 0) - txs.AddRange(standardTransactions); - - return new BlockBody + return new Block { - Transactions = txs, - RewardedSignatures = null, + _version = 2, + _blockV2 = block, + + Hash = block.Hash, + + AccumulatedSeed = block.Header.AccumulatedSeed, + EraId = block.Header.EraId, + Height = block.Header.Height, + ParentHash = block.Header.ParentHash, + ProtocolVersion = block.Header.ProtocolVersion, + StateRootHash = block.Header.StateRootHash, + Timestamp = block.Header.Timestamp, + RandomBit = block.Header.RandomBit, + CurrentGasPrice = block.Header.CurrentGasPrice, + LastSwitchBlockHash = block.Header.LastSwitchBlockHash, + Proposer = block.Header.Proposer, + EraEnd = block.Header.EraEnd, + + Transactions = block.Body.Transactions, + RewardedSignatures = block.Body.RewardedSignatures, }; } } - - /// - /// A block in the network - /// - internal class BlockV2 - { - /// - /// Block hash - /// - [JsonPropertyName("hash")] - public string Hash { get; init; } - - /// - /// Block header - /// - [JsonPropertyName("header")] - public BlockHeader Header { get; init; } - - /// - /// Block body - /// - [JsonPropertyName("body")] - public BlockBody Body { get; init; } - } - - /// - /// A validator's public key paired with a corresponding signature of a given block hash. - /// - public class BlockProof - { - /// - /// The validator's public key. - /// - [JsonPropertyName("public_key")] - [JsonConverter(typeof(PublicKey.PublicKeyConverter))] - public PublicKey PublicKey { get; init; } - - /// - /// The validator's signature. - /// - [JsonPropertyName("signature")] - [JsonConverter(typeof(Signature.SignatureConverter))] - public Signature Signature { get; init; } - } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/BlockProof.cs b/Casper.Network.SDK/Types/BlockProof.cs new file mode 100644 index 0000000..2ff0ea1 --- /dev/null +++ b/Casper.Network.SDK/Types/BlockProof.cs @@ -0,0 +1,24 @@ +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + /// + /// A validator's public key paired with a corresponding signature of a given block hash. + /// + public class BlockProof + { + /// + /// The validator's public key. + /// + [JsonPropertyName("public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey PublicKey { get; init; } + + /// + /// The validator's signature. + /// + [JsonPropertyName("signature")] + [JsonConverter(typeof(Signature.SignatureConverter))] + public Signature Signature { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/EraEnd.cs b/Casper.Network.SDK/Types/EraEnd.cs index 38bdabd..286abe8 100644 --- a/Casper.Network.SDK/Types/EraEnd.cs +++ b/Casper.Network.SDK/Types/EraEnd.cs @@ -108,4 +108,11 @@ public class EraEndV2 [JsonPropertyName("next_era_gas_price")] public UInt16 NextEraGasPrice { get; init; } } + + /// + /// Information related to the end of an era, and validator weights for the following era (alias for EraEndV2). + /// + public class EraEnd: EraEndV2 + { + } } \ No newline at end of file From dfdf7bc49d872c4e5b09d4ff9fea9ff386db327f Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 26 Jun 2024 11:32:51 +0200 Subject: [PATCH 039/126] Fix parsing of Step server event. Signed-off-by: David Hernando --- Casper.Network.SDK/SSE/Step.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Casper.Network.SDK/SSE/Step.cs b/Casper.Network.SDK/SSE/Step.cs index 0f39c31..8fea834 100644 --- a/Casper.Network.SDK/SSE/Step.cs +++ b/Casper.Network.SDK/SSE/Step.cs @@ -1,4 +1,6 @@ +using System.Collections.Generic; using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; namespace Casper.Network.SDK.Types { @@ -16,7 +18,8 @@ public class Step /// /// The effect of executing the deploy. /// - [JsonPropertyName("execution_effect")] - public ExecutionEffect Effect { get; init; } + [JsonPropertyName("execution_effects")] + [JsonConverter(typeof(GenericListConverter))] + public List Effects { get; init; } } } \ No newline at end of file From 8175a73766fe19313707101b12433c3eb347d400 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 26 Jun 2024 16:46:09 +0200 Subject: [PATCH 040/126] fixed BlockHeader header in QueryGlobalStateResult . Signed-off-by: David Hernando --- .../ResultTypes/QueryGlobalStateResult.cs | 1 + Casper.Network.SDK/Types/Block.cs | 198 +++++++++++++++++- 2 files changed, 196 insertions(+), 3 deletions(-) diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs index 3b02e40..6faaf6e 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/QueryGlobalStateResult.cs @@ -12,6 +12,7 @@ public class QueryGlobalStateResult : RpcResult /// The block header if a Block hash was provided. /// [JsonPropertyName("block_header")] + [JsonConverter(typeof(BlockHeader.BlockHeaderConverter))] public BlockHeader BlockHeader { get; init; } /// diff --git a/Casper.Network.SDK/Types/Block.cs b/Casper.Network.SDK/Types/Block.cs index be0f41a..3b3aeb9 100644 --- a/Casper.Network.SDK/Types/Block.cs +++ b/Casper.Network.SDK/Types/Block.cs @@ -101,8 +101,200 @@ public class BlockHeaderV2 : BlockHeaderV1 /// /// A block header (alias for BlockHeaderV2) /// - public class BlockHeader : BlockHeaderV2 + public class BlockHeader { + protected int _version; + + /// + /// Returns the version of the block. + /// + public int Version + { + get { return _version; } + } + + protected BlockHeaderV1 _blockHeaderV1; + + protected BlockHeaderV2 _blockHeaderV2; + + /// + /// A seed needed for initializing a future era. + /// + public string AccumulatedSeed { get; init; } + + /// + /// The hash of the block's body. + /// + public string BodyHash { get; init; } + + /// + /// The era ID in which this block was created. + /// + public ulong EraId { get; init; } + + /// + /// The height of this block, i.e. the number of ancestors. + /// + public ulong Height { get; init; } + + /// + /// The parent block's hash. + /// + public string ParentHash { get; init; } + + /// + /// The protocol version of the network from when this block was created. + /// + public string ProtocolVersion { get; init; } + + /// + /// A random bit needed for initializing a future era. + /// + public bool RandomBit { get; init; } + + /// + /// The root hash of global state after the deploys in this block have been executed. + /// + public string StateRootHash { get; init; } + + /// + /// The timestamp from when the block was proposed. + /// + public string Timestamp { get; init; } + + /// + /// The `EraEnd` of a block if it is a switch block. + /// + public EraEnd EraEnd { get; init; } + + /// + /// The gas price of the era. + /// + public UInt16 CurrentGasPrice { get; init; } + + /// + /// Public key of the validator that proposed the block + /// + public Proposer Proposer { get; init; } + + public string LastSwitchBlockHash { get; init; } + + public class BlockHeaderConverter : JsonConverter + { + public override BlockHeader Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + reader.Read(); + var version = reader.GetString(); + reader.Read(); + switch (version) + { + case "Version1": + var blockHeaderv1 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return (BlockHeader)blockHeaderv1; + case "Version2": + var blockHeaderv2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return (BlockHeader)blockHeaderv2; + default: + throw new JsonException("Expected Version1 or Version2"); + } + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + BlockHeader blockHeader, + JsonSerializerOptions options) + { + switch (blockHeader.Version) + { + case 1: + writer.WritePropertyName("Version1"); + writer.WriteStartObject(); + JsonSerializer.Serialize((BlockHeaderV1)blockHeader, options); + writer.WriteEndObject(); + break; + case 2: + writer.WritePropertyName("Version2"); + writer.WriteStartObject(); + JsonSerializer.Serialize((BlockHeaderV2)blockHeader, options); + writer.WriteEndObject(); + break; + default: + throw new JsonException($"Unexpected block header version {blockHeader.Version}"); + } + } + } + + public static explicit operator BlockHeaderV1(BlockHeader blockHeader) + { + if(blockHeader._version == 1) + return blockHeader._blockHeaderV1; + + throw new InvalidCastException("Version2 block header cannot be converted to Version1"); + } + + public static explicit operator BlockHeaderV2(BlockHeader blockHeader) + { + if(blockHeader._version == 2) + return blockHeader._blockHeaderV2; + + throw new InvalidCastException("Version1 block header cannot be converted to Version2"); + } + + public static explicit operator BlockHeader(BlockHeaderV1 blockHeader) + { + return new BlockHeader + { + _version = 1, + _blockHeaderV1 = blockHeader, + + AccumulatedSeed = blockHeader.AccumulatedSeed, + EraId = blockHeader.EraId, + Height = blockHeader.Height, + ParentHash = blockHeader.ParentHash, + ProtocolVersion = blockHeader.ProtocolVersion, + StateRootHash = blockHeader.StateRootHash, + Timestamp = blockHeader.Timestamp, + RandomBit = blockHeader.RandomBit, + CurrentGasPrice = 1, + LastSwitchBlockHash = null, + Proposer = null, + EraEnd = BlockV1.EraEndFromV1(blockHeader), + }; + } + + public static explicit operator BlockHeader(BlockHeaderV2 blockHeader) + { + return new BlockHeader + { + _version = 2, + _blockHeaderV2 = blockHeader, + + AccumulatedSeed = blockHeader.AccumulatedSeed, + EraId = blockHeader.EraId, + Height = blockHeader.Height, + ParentHash = blockHeader.ParentHash, + ProtocolVersion = blockHeader.ProtocolVersion, + StateRootHash = blockHeader.StateRootHash, + Timestamp = blockHeader.Timestamp, + RandomBit = blockHeader.RandomBit, + CurrentGasPrice = blockHeader.CurrentGasPrice, + LastSwitchBlockHash = blockHeader.LastSwitchBlockHash, + Proposer = blockHeader.Proposer, + EraEnd = blockHeader.EraEnd, + }; + } } /// @@ -236,7 +428,7 @@ public class BlockV1 [JsonPropertyName("body")] public BlockBodyV1 Body { get; init; } - internal static EraEnd EraEndFromV1(BlockHeaderV1 header, BlockBodyV1 body) + internal static EraEnd EraEndFromV1(BlockHeaderV1 header) { Dictionary> rewards = new(); if (header.EraEnd != null && header.EraEnd.EraReport.Rewards.Count > 0) @@ -570,7 +762,7 @@ public static explicit operator Block(BlockV1 block) CurrentGasPrice = 1, LastSwitchBlockHash = null, Proposer = block.Body.Proposer, - EraEnd = BlockV1.EraEndFromV1(block.Header, block.Body), + EraEnd = BlockV1.EraEndFromV1(block.Header), Transactions = BlockV1.BlockTransactionsFromV1(block.Body), RewardedSignatures = null, From b911529ad9ba77d8900d1ea660d623dc6fe16494 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 28 Jun 2024 15:10:42 +0200 Subject: [PATCH 041/126] fix bug parsing Version1.ExecutoinResults Signed-off-by: David Hernando --- .../RPCResponses/GetDeployResultTest.cs | 79 +++ .../get-deploy-result-version1-failure.json | 245 +++++++ .../get-deploy-result-version1-success.json | 632 ++++++++++++++++++ .../get-deploy-result-version2-success.json | 225 +++++++ Casper.Network.SDK/Types/ExecutionResult.cs | 5 +- 5 files changed, 1185 insertions(+), 1 deletion(-) create mode 100644 Casper.Network.SDK.Test/RPCResponses/GetDeployResultTest.cs create mode 100644 Casper.Network.SDK.Test/TestData/get-deploy-result-version1-failure.json create mode 100644 Casper.Network.SDK.Test/TestData/get-deploy-result-version1-success.json create mode 100644 Casper.Network.SDK.Test/TestData/get-deploy-result-version2-success.json diff --git a/Casper.Network.SDK.Test/RPCResponses/GetDeployResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetDeployResultTest.cs new file mode 100644 index 0000000..be2ae0d --- /dev/null +++ b/Casper.Network.SDK.Test/RPCResponses/GetDeployResultTest.cs @@ -0,0 +1,79 @@ +using System.IO; +using System.Linq; +using Casper.Network.SDK.JsonRpc.ResultTypes; +using Casper.Network.SDK.Types; +using NUnit.Framework; +using Org.BouncyCastle.Utilities.Encoders; + +namespace NetCasperTest.RPCResponses +{ + public class GetDeployResultTest + { + [Test] + public void GetDeployResultTest_Version1_Success() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-deploy-result-version1-success.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + + var deploy = result.Deploy; + AssertExtensions.IsHash(deploy.Hash); + AssertExtensions.IsValidHex(deploy.Header.Account.ToAccountHex(), 33); + Assert.AreEqual(1, deploy.Payment.RuntimeArgs.Count); + Assert.AreEqual(6, deploy.Session.RuntimeArgs.Count); + Assert.AreEqual(1, deploy.Approvals.Count); + AssertExtensions.IsHash(result.ExecutionInfo.BlockHash); + Assert.AreEqual(23, result.ExecutionInfo.BlockHeight); + Assert.IsTrue(result.ExecutionInfo.ExecutionResult.Effect.Count > 0); + Assert.IsNull(result.ExecutionInfo.ExecutionResult.ErrorMessage); + } + + [Test] + public void GetDeployResultTest_Version1_Failure() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-deploy-result-version1-failure.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + + var deploy = result.Deploy; + AssertExtensions.IsHash(deploy.Hash); + AssertExtensions.IsValidHex(deploy.Header.Account.ToAccountHex(), 33); + Assert.AreEqual(1, deploy.Payment.RuntimeArgs.Count); + Assert.AreEqual(2, deploy.Session.RuntimeArgs.Count); + Assert.AreEqual(1, deploy.Approvals.Count); + AssertExtensions.IsHash(result.ExecutionInfo.BlockHash); + Assert.AreEqual(122, result.ExecutionInfo.BlockHeight); + Assert.IsTrue(result.ExecutionInfo.ExecutionResult.Effect.Count > 0); + Assert.AreEqual("User error: 60001", result.ExecutionInfo.ExecutionResult.ErrorMessage); + Assert.AreEqual("31057410", result.ExecutionInfo.ExecutionResult.Cost.ToString()); + } + + [Test] + public void GetDeployResultTest_Version2_Success() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-deploy-result-version2-success.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + + var deploy = result.Deploy; + AssertExtensions.IsHash(deploy.Hash); + AssertExtensions.IsValidHex(deploy.Header.Account.ToAccountHex(), 33); + Assert.AreEqual(1, deploy.Payment.RuntimeArgs.Count); + Assert.AreEqual(2, deploy.Session.RuntimeArgs.Count); + Assert.AreEqual(1, deploy.Approvals.Count); + AssertExtensions.IsHash(result.ExecutionInfo.BlockHash); + Assert.AreEqual(1964, result.ExecutionInfo.BlockHeight); + Assert.IsTrue(result.ExecutionInfo.ExecutionResult.Effect.Count > 0); + Assert.IsNull(result.ExecutionInfo.ExecutionResult.ErrorMessage); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/get-deploy-result-version1-failure.json b/Casper.Network.SDK.Test/TestData/get-deploy-result-version1-failure.json new file mode 100644 index 0000000..7baf554 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-deploy-result-version1-failure.json @@ -0,0 +1,245 @@ +{ + "api_version": "2.0.0", + "deploy": { + "hash": "f21957f06b05720988672e625d9900f4d787524d05e4dbb30b04db18625aeac7", + "header": { + "account": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", + "timestamp": "2024-06-28T10:22:35.711Z", + "ttl": "3m", + "gas_price": 1, + "body_hash": "3cd24b57053a21605a0569edcd0da556782db67d5ee0529dedecc9e1271d34d6", + "dependencies": [], + "chain_name": "casper-net-1" + }, + "payment": { + "ModuleBytes": { + "module_bytes": "", + "args": [ + [ + "amount", + { + "cl_type": "U512", + "bytes": "0500f2052a01", + "parsed": "5000000000" + } + ] + ] + } + }, + "session": { + "StoredVersionedContractByHash": { + "hash": "2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "version": null, + "entry_point": "transfer", + "args": [ + [ + "recipient", + { + "cl_type": "Key", + "bytes": "006bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a", + "parsed": "account-hash-6bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a" + } + ], + [ + "amount", + { + "cl_type": "U256", + "bytes": "02d430", + "parsed": "12500" + } + ] + ] + } + }, + "approvals": [ + { + "signer": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", + "signature": "0188dd1e8400c8515c42df0184e11316b6eee94194a9be41b9bee9b4ce48304643a5bbb7cf5511649597f46c41c7a543882a09900244f65b82abad2bf906e9b20b" + } + ] + }, + "execution_info": { + "block_hash": "fee17570df0ee6c77ba19cc4194490f1f939ba7823a6a3e664c5599dc7067879", + "block_height": 122, + "execution_result": { + "Version1": { + "Failure": { + "effect": { + "operations": [], + "transforms": [ + { + "key": "account-hash-6174cf2e6f8fed1715c9a3bace9c50bfe572eecb763b0ed3f644532616452008", + "transform": "Identity" + }, + { + "key": "hash-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-d6513eb8e21953badd438f0e3cb01fa4c47f879cd6ecd28a25bd327cb0d0a0e2", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "hash-1499242da9799e647edf8e0903e75776fe5c1df56a26de15d028422ef1f2bf93", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "balance-f34ede14ef9d3adc031b37ae4473f6818f289b786083ce589343cf52a8f59841", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "balance-f34ede14ef9d3adc031b37ae4473f6818f289b786083ce589343cf52a8f59841", + "transform": { + "WriteCLValue": { + "cl_type": "U512", + "bytes": "0e000efad5085bc138938d44c64d31", + "parsed": "999999999999999999999995000000000" + } + } + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": { + "AddUInt512": "5000000000" + } + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-d6513eb8e21953badd438f0e3cb01fa4c47f879cd6ecd28a25bd327cb0d0a0e2", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "hash-1499242da9799e647edf8e0903e75776fe5c1df56a26de15d028422ef1f2bf93", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "balance-f34ede14ef9d3adc031b37ae4473f6818f289b786083ce589343cf52a8f59841", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": { + "WriteCLValue": { + "cl_type": "U512", + "bytes": "045419d004", + "parsed": "80746836" + } + } + }, + { + "key": "balance-f34ede14ef9d3adc031b37ae4473f6818f289b786083ce589343cf52a8f59841", + "transform": { + "AddUInt512": "4919253164" + } + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "hash-1499242da9799e647edf8e0903e75776fe5c1df56a26de15d028422ef1f2bf93", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "balance-59621579e9cd67523b65d2ac87fe05993be7dfd6ba677fd1b4200e64a53f9d16", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": { + "WriteCLValue": { + "cl_type": "U512", + "bytes": "00", + "parsed": "0" + } + } + }, + { + "key": "balance-59621579e9cd67523b65d2ac87fe05993be7dfd6ba677fd1b4200e64a53f9d16", + "transform": { + "AddUInt512": "80746836" + } + } + ] + }, + "transfers": [], + "cost": "31057410", + "error_message": "User error: 60001" + } + } + } + } +} + + diff --git a/Casper.Network.SDK.Test/TestData/get-deploy-result-version1-success.json b/Casper.Network.SDK.Test/TestData/get-deploy-result-version1-success.json new file mode 100644 index 0000000..4ce6f3d --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-deploy-result-version1-success.json @@ -0,0 +1,632 @@ +{ + "api_version": "2.0.0", + "deploy": { + "hash": "cdbb52d946ce58dccde35ac17a5820c0ee6bdddcf80d0e5e22572d2be6bffed6", + "header": { + "account": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55", + "timestamp": "2024-06-28T10:15:36.909Z", + "ttl": "30m", + "gas_price": 1, + "body_hash": "467d34798eb4075f9518a82f7a8eb959290f00ac5fb305c3428f63508082ba39", + "dependencies": [], + "chain_name": "casper-net-1" + }, + "payment": { + "ModuleBytes": { + "module_bytes": "", + "args": [ + [ + "amount", + { + "cl_type": "U512", + "bytes": "050088526a74", + "parsed": "500000000000" + } + ] + ] + } + }, + "session": { + "ModuleBytes": { + "module_bytes": "0061736d0100000141073602b803200141b8036a10ee808080002102200141b8036a0002100000", + "args": [ + [ + "name", + { + "cl_type": "String", + "bytes": "0b000000434c49434b542054657374", + "parsed": "CLICKT Test" + } + ], + [ + "symbol", + { + "cl_type": "String", + "bytes": "06000000434c49434b54", + "parsed": "CLICKT" + } + ], + [ + "decimals", + { + "cl_type": "U8", + "bytes": "03", + "parsed": 3 + } + ], + [ + "total_supply", + { + "cl_type": "U256", + "bytes": "0400ca9a3b", + "parsed": "1000000000" + } + ], + [ + "events_mode", + { + "cl_type": "U8", + "bytes": "01", + "parsed": 1 + } + ], + [ + "enable_mint_burn", + { + "cl_type": "U8", + "bytes": "00", + "parsed": 0 + } + ] + ] + } + }, + "approvals": [ + { + "signer": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55", + "signature": "01cddbf5fc583f7ebf2293837e8f306ecb3650ad8df3760010048e3a7ad75640dcce3e5b1c990cd596f60b01570fb963514c0af083a29b81194f708a777f15910b" + } + ] + }, + "execution_info": { + "block_hash": "63dcce7f471030454da37a84e35c62fac863ee73aea0a9bf159e973ecc3e2e7d", + "block_height": 23, + "execution_result": { + "Version1": { + "Success": { + "effect": { + "operations": [], + "transforms": [ + { + "key": "account-hash-6174cf2e6f8fed1715c9a3bace9c50bfe572eecb763b0ed3f644532616452008", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-d6513eb8e21953badd438f0e3cb01fa4c47f879cd6ecd28a25bd327cb0d0a0e2", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "hash-1499242da9799e647edf8e0903e75776fe5c1df56a26de15d028422ef1f2bf93", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "balance-bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "balance-bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e", + "transform": { + "WriteCLValue": { + "cl_type": "U512", + "bytes": "0e0078ad95955ac138938d44c64d31", + "parsed": "999999999999999999999500000000000" + } + } + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": { + "AddUInt512": "500000000000" + } + }, + { + "key": "uref-db1891fef9155d6ef077d8a93d5e3edcba5eea0d8298163fb9872e133268711e-000", + "transform": { + "WriteCLValue": { + "cl_type": "String", + "bytes": "0b000000434c49434b542054657374", + "parsed": "CLICKT Test" + } + } + }, + { + "key": "uref-e8d05c77d4bf5d46001bad5828c27d90b5da6e4a151496e46af1cc706efd48dd-000", + "transform": { + "WriteCLValue": { + "cl_type": "String", + "bytes": "06000000434c49434b54", + "parsed": "CLICKT" + } + } + }, + { + "key": "uref-21fe1e848187fdd960698ad8b7986d5c0e08de270b6757e7635c9d86e44e1f9e-000", + "transform": { + "WriteCLValue": { + "cl_type": "U8", + "bytes": "03", + "parsed": 3 + } + } + }, + { + "key": "uref-18556a7a9e30862aa6f649f184febae6edf1ffe038403ef85a8ff78ad97307b9-000", + "transform": { + "WriteCLValue": { + "cl_type": "U256", + "bytes": "0400ca9a3b", + "parsed": "1000000000" + } + } + }, + { + "key": "uref-3d67308514d1a0c447a07f088b165d54aa13f601f9e58791e059bc6344cd84f9-000", + "transform": { + "WriteCLValue": { + "cl_type": "U8", + "bytes": "01", + "parsed": 1 + } + } + }, + { + "key": "uref-5c12c6fa083d7a2340379cb9a56d07915f186d76624c7223c478a679962cecf4-000", + "transform": { + "WriteCLValue": { + "cl_type": "U8", + "bytes": "00", + "parsed": 0 + } + } + }, + { + "key": "uref-783e3cbee2b8acb3e58da315fdfdc809faf98f0c03d4de7b7d3fd1ec10a7bd6e-000", + "transform": { + "WriteCLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + }, + { + "key": "hash-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "transform": "WriteContractPackage" + }, + { + "key": "account-hash-56befc13a6fd62e18f361700a5e08f966901c34df8041b36ec97d54d605c23de", + "transform": { + "AddKeys": [ + { + "name": "cep18_contract_package_CLICKT Test", + "key": "hash-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb" + } + ] + } + }, + { + "key": "account-hash-56befc13a6fd62e18f361700a5e08f966901c34df8041b36ec97d54d605c23de", + "transform": { + "AddKeys": [ + { + "name": "cep18_contract_package_access_CLICKT Test", + "key": "uref-783e3cbee2b8acb3e58da315fdfdc809faf98f0c03d4de7b7d3fd1ec10a7bd6e-007" + } + ] + } + }, + { + "key": "hash-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "transform": "Identity" + }, + { + "key": "hash-906aa4db02430ae0894f6996c3dcb423013d9878037ad84a46c6f6ac78b7d5b1", + "transform": "WriteContractWasm" + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": "WriteContract" + }, + { + "key": "hash-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "transform": "WriteContractPackage" + }, + { + "key": "account-hash-56befc13a6fd62e18f361700a5e08f966901c34df8041b36ec97d54d605c23de", + "transform": { + "AddKeys": [ + { + "name": "cep18_contract_hash_CLICKT Test", + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01" + } + ] + } + }, + { + "key": "uref-09377f91048ec8d1b15df45c151e55683d8b93a779c5f2cef634d3ca1543b192-000", + "transform": { + "WriteCLValue": { + "cl_type": "U32", + "bytes": "01000000", + "parsed": 1 + } + } + }, + { + "key": "account-hash-56befc13a6fd62e18f361700a5e08f966901c34df8041b36ec97d54d605c23de", + "transform": { + "AddKeys": [ + { + "name": "cep18_contract_version_CLICKT Test", + "key": "uref-09377f91048ec8d1b15df45c151e55683d8b93a779c5f2cef634d3ca1543b192-007" + } + ] + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": "Identity" + }, + { + "key": "hash-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "transform": "Identity" + }, + { + "key": "hash-906aa4db02430ae0894f6996c3dcb423013d9878037ad84a46c6f6ac78b7d5b1", + "transform": "Identity" + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "package_hash", + "key": "hash-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb" + } + ] + } + }, + { + "key": "uref-ae50771df34a40472b425ddd98c874a0836888f3bd22e01842f7bfddb3549c6f-000", + "transform": { + "WriteCLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "allowances", + "key": "uref-ae50771df34a40472b425ddd98c874a0836888f3bd22e01842f7bfddb3549c6f-007" + } + ] + } + }, + { + "key": "uref-aeae29762c24430dd4e6dd0ff93994abd53c43907103a27bfe7d3881fdcebbc0-000", + "transform": { + "WriteCLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "balances", + "key": "uref-aeae29762c24430dd4e6dd0ff93994abd53c43907103a27bfe7d3881fdcebbc0-007" + } + ] + } + }, + { + "key": "dictionary-3242adf99a374c325bbc98d29323805a5a0c7d81c0b55731027111bc1b220869", + "transform": { + "WriteCLValue": { + "cl_type": "Any", + "bytes": "050000000400ca9a3b0720000000aeae29762c24430dd4e6dd0ff93994abd53c43907103a27bfe7d3881fdcebbc02c0000004146612b2f424f6d2f574c686a7a5958414b58676a355a7041634e4e2b4151624e7579583155316758435065", + "parsed": null + } + } + }, + { + "key": "uref-304e0283e0bd6fbb04dd97af1c6caea28cbd7a8937fcf46724052142cfe3f603-000", + "transform": { + "WriteCLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "security_badges", + "key": "uref-304e0283e0bd6fbb04dd97af1c6caea28cbd7a8937fcf46724052142cfe3f603-007" + } + ] + } + }, + { + "key": "dictionary-fbde437d8e6d72b1eecd9e776d5a54c70216381905dca0ef5e589cf6d708d5d1", + "transform": { + "WriteCLValue": { + "cl_type": "Any", + "bytes": "01000000000320000000304e0283e0bd6fbb04dd97af1c6caea28cbd7a8937fcf46724052142cfe3f6032c0000004146612b2f424f6d2f574c686a7a5958414b58676a355a7041634e4e2b4151624e7579583155316758435065", + "parsed": null + } + } + }, + { + "key": "uref-3d67308514d1a0c447a07f088b165d54aa13f601f9e58791e059bc6344cd84f9-000", + "transform": "Identity" + }, + { + "key": "uref-83fe09fd54cc79ce67e513bdef50297936fdcef11fa7886f984a373aa2fa000c-000", + "transform": { + "WriteCLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "__events", + "key": "uref-83fe09fd54cc79ce67e513bdef50297936fdcef11fa7886f984a373aa2fa000c-007" + } + ] + } + }, + { + "key": "uref-c241213422d3c9adb0afa8437ea3dab8c69dd863c0cf44536d7586538545b482-000", + "transform": { + "WriteCLValue": { + "cl_type": "U32", + "bytes": "00000000", + "parsed": 0 + } + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "__events_length", + "key": "uref-c241213422d3c9adb0afa8437ea3dab8c69dd863c0cf44536d7586538545b482-007" + } + ] + } + }, + { + "key": "uref-8ed3033a37c622f23a4c1689c29a3b773bec3a8ac8a583e866c3d196a7b2e8a4-000", + "transform": { + "WriteCLValue": { + "cl_type": { + "Map": { + "key": "String", + "value": { + "List": { + "Tuple2": [ + "String", + "Any" + ] + } + } + } + }, + "bytes": "08000000040000004275726e02000000050000006f776e65720b06000000616d6f756e74070e0000004368616e67655365637572697479020000000500000061646d696e0b0e0000007365635f6368616e67655f6d6170110b03110000004465637265617365416c6c6f77616e636504000000050000006f776e65720b070000007370656e6465720b09000000616c6c6f77616e63650707000000646563725f62790711000000496e637265617365416c6c6f77616e636504000000050000006f776e65720b070000007370656e6465720b09000000616c6c6f77616e63650706000000696e635f627907040000004d696e740200000009000000726563697069656e740b06000000616d6f756e74070c000000536574416c6c6f77616e636503000000050000006f776e65720b070000007370656e6465720b09000000616c6c6f77616e636507080000005472616e73666572030000000600000073656e6465720b09000000726563697069656e740b06000000616d6f756e74070c0000005472616e7366657246726f6d04000000070000007370656e6465720b050000006f776e65720b09000000726563697069656e740b06000000616d6f756e7407", + "parsed": null + } + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "__events_schema", + "key": "uref-8ed3033a37c622f23a4c1689c29a3b773bec3a8ac8a583e866c3d196a7b2e8a4-007" + } + ] + } + }, + { + "key": "uref-6533dbce8d122ee0310a51dc4416e1a594277ec8625f1df54cbc1dcccae27c4b-000", + "transform": { + "WriteCLValue": { + "cl_type": "String", + "bytes": "03000000312e31", + "parsed": "1.1" + } + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "__events_ces_version", + "key": "uref-6533dbce8d122ee0310a51dc4416e1a594277ec8625f1df54cbc1dcccae27c4b-007" + } + ] + } + }, + { + "key": "deploy-cdbb52d946ce58dccde35ac17a5820c0ee6bdddcf80d0e5e22572d2be6bffed6", + "transform": { + "WriteDeployInfo": { + "deploy_hash": "cdbb52d946ce58dccde35ac17a5820c0ee6bdddcf80d0e5e22572d2be6bffed6", + "transfers": [], + "from": "account-hash-56befc13a6fd62e18f361700a5e08f966901c34df8041b36ec97d54d605c23de", + "source": "uref-bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e-007", + "gas": "239918433115" + } + } + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-d6513eb8e21953badd438f0e3cb01fa4c47f879cd6ecd28a25bd327cb0d0a0e2", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "account-hash-56befc13a6fd62e18f361700a5e08f966901c34df8041b36ec97d54d605c23de", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "hash-1499242da9799e647edf8e0903e75776fe5c1df56a26de15d028422ef1f2bf93", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "balance-bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": { + "WriteCLValue": { + "cl_type": "U512", + "bytes": "05900f467738", + "parsed": "242519248784" + } + } + }, + { + "key": "balance-bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e", + "transform": { + "AddUInt512": "257480751216" + } + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "hash-1499242da9799e647edf8e0903e75776fe5c1df56a26de15d028422ef1f2bf93", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "balance-59621579e9cd67523b65d2ac87fe05993be7dfd6ba677fd1b4200e64a53f9d16", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": { + "WriteCLValue": { + "cl_type": "U512", + "bytes": "00", + "parsed": "0" + } + } + }, + { + "key": "balance-59621579e9cd67523b65d2ac87fe05993be7dfd6ba677fd1b4200e64a53f9d16", + "transform": { + "AddUInt512": "242519248784" + } + } + ] + }, + "transfers": [], + "cost": "239918433115" + } + } + } + } +} + + diff --git a/Casper.Network.SDK.Test/TestData/get-deploy-result-version2-success.json b/Casper.Network.SDK.Test/TestData/get-deploy-result-version2-success.json new file mode 100644 index 0000000..6b31829 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-deploy-result-version2-success.json @@ -0,0 +1,225 @@ +{ + "api_version": "2.0.0", + "deploy": { + "hash": "8738a51ca5c656e1c3eea5d4473fd07f92d6bb7dfd2d75d8d8dc59331545096c", + "header": { + "account": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55", + "timestamp": "2024-06-28T13:05:19.722Z", + "ttl": "3m", + "gas_price": 1, + "body_hash": "3cd24b57053a21605a0569edcd0da556782db67d5ee0529dedecc9e1271d34d6", + "dependencies": [], + "chain_name": "casper-net-1" + }, + "payment": { + "ModuleBytes": { + "module_bytes": "", + "args": [ + [ + "amount", + { + "cl_type": "U512", + "bytes": "0500f2052a01", + "parsed": "5000000000" + } + ] + ] + } + }, + "session": { + "StoredVersionedContractByHash": { + "hash": "2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "version": null, + "entry_point": "transfer", + "args": [ + [ + "recipient", + { + "cl_type": "Key", + "bytes": "006bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a", + "parsed": "account-hash-6bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a" + } + ], + [ + "amount", + { + "cl_type": "U256", + "bytes": "02d430", + "parsed": "12500" + } + ] + ] + } + }, + "approvals": [ + { + "signer": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55", + "signature": "01c5caded9fd67da7232ad55b1fe3f6781ee5212c3c71b755866d63543d014a2501dece11e3f3c1073b919dcaf41b09d1709b3da7caae07bbbb4eb2d01876c7c0a" + } + ] + }, + "execution_info": { + "block_hash": "638d6a36843f153f173810569ca41129228d49e9f786bb96cd59c0215c2101b6", + "block_height": 1964, + "execution_result": { + "Version2": { + "initiator": { + "PublicKey": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55" + }, + "error_message": null, + "limit": "500000000000", + "consumed": "537061384", + "cost": "500000000000", + "payment": [], + "transfers": [], + "size_estimate": 392, + "effects": [ + { + "key": "balance-hold-01bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e1d4ef35e90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "050088526a74", + "parsed": "500000000000" + } + } + } + }, + { + "key": "package-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "kind": "Identity" + }, + { + "key": "entity-contract-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "kind": "Identity" + }, + { + "key": "package-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "kind": "Identity" + }, + { + "key": "entry-point-v1-entity-contract-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01-3820ce25e54df0470fb738e3e0f63ee50b2719cf2680da2bdb579e21aebc8f63", + "kind": "Identity" + }, + { + "key": "byte-code-v1-wasm-906aa4db02430ae0894f6996c3dcb423013d9878037ad84a46c6f6ac78b7d5b1", + "kind": "Identity" + }, + { + "key": "dictionary-3242adf99a374c325bbc98d29323805a5a0c7d81c0b55731027111bc1b220869", + "kind": "Identity" + }, + { + "key": "dictionary-5f00faae08f256fdbbcab53942ae89f0961dbc28142c49d3f6fc7efb827887cf", + "kind": "Identity" + }, + { + "key": "dictionary-3242adf99a374c325bbc98d29323805a5a0c7d81c0b55731027111bc1b220869", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "050000000484379a3b0720000000aeae29762c24430dd4e6dd0ff93994abd53c43907103a27bfe7d3881fdcebbc02c0000004146612b2f424f6d2f574c686a7a5958414b58676a355a7041634e4e2b4151624e7579583155316758435065", + "parsed": null + } + } + } + }, + { + "key": "dictionary-5f00faae08f256fdbbcab53942ae89f0961dbc28142c49d3f6fc7efb827887cf", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "03000000027c920720000000aeae29762c24430dd4e6dd0ff93994abd53c43907103a27bfe7d3881fdcebbc02c0000004147764d746b7035424c636866645474665a35425933686634684d3952644f514a5173592b5551704637774b", + "parsed": null + } + } + } + }, + { + "key": "uref-3d67308514d1a0c447a07f088b165d54aa13f601f9e58791e059bc6344cd84f9-000", + "kind": "Identity" + }, + { + "key": "uref-c241213422d3c9adb0afa8437ea3dab8c69dd863c0cf44536d7586538545b482-000", + "kind": "Identity" + }, + { + "key": "dictionary-b8404730e4b5686f96941958a9aabab4ee4997b2333cee681e96ed986d159107", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "5b000000570000000e0000006576656e745f5472616e736665720056befc13a6fd62e18f361700a5e08f966901c34df8041b36ec97d54d605c23de006bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a02d4300e032000000083fe09fd54cc79ce67e513bdef50297936fdcef11fa7886f984a373aa2fa000c0100000032", + "parsed": null + } + } + } + }, + { + "key": "uref-c241213422d3c9adb0afa8437ea3dab8c69dd863c0cf44536d7586538545b482-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U32", + "bytes": "03000000", + "parsed": 3 + } + } + } + }, + { + "key": "balance-hold-01bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e1d4ef35e90010000", + "kind": { + "Prune": "balance-hold-01bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e1d4ef35e90010000" + } + }, + { + "key": "balance-hold-00bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e1d4ef35e90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "050088526a74", + "parsed": "500000000000" + } + } + } + }, + { + "key": "entity-system-96d9230e1a4e2154023d3d8f581a9f0503bae015907aec8debdac83054e3c019", + "kind": "Identity" + }, + { + "key": "entity-system-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "kind": "Identity" + }, + { + "key": "entity-system-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "kind": "Identity" + }, + { + "key": "bid-addr-011c50d14ca563d5afe0399a3da010bac01383f502bc7cce774cc03d858a94cf06", + "kind": "Identity" + }, + { + "key": "bid-addr-041c50d14ca563d5afe0399a3da010bac01383f502bc7cce774cc03d858a94cf06ba00000000000000", + "kind": { + "Write": { + "BidKind": { + "Credit": { + "validator_public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "era_id": 186, + "amount": "500000000000" + } + } + } + } + } + ] + } + } + } +} diff --git a/Casper.Network.SDK/Types/ExecutionResult.cs b/Casper.Network.SDK/Types/ExecutionResult.cs index 0324b14..f00748c 100644 --- a/Casper.Network.SDK/Types/ExecutionResult.cs +++ b/Casper.Network.SDK/Types/ExecutionResult.cs @@ -126,7 +126,10 @@ public override ExecutionResult Read( switch (version) { case "Version1": - var erV1 = JsonSerializer.Deserialize(ref reader, options); + var erSerializerOptions = new JsonSerializerOptions(options); + erSerializerOptions.Converters.Add(new ExecutionResultV1.ExecutionResultV1Converter()); + + var erV1 = JsonSerializer.Deserialize(ref reader, erSerializerOptions); reader.Read(); return (ExecutionResult)erV1; case "Version2": From 6e9fc69c233c2d1ec23651569e5c45c8820b5234 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 28 Jun 2024 15:27:24 +0200 Subject: [PATCH 042/126] fix bug parsing Version1.ExecutoinResults Signed-off-by: David Hernando --- Casper.Network.SDK/NetCasperClient.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index d783cf0..5a885f6 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -568,6 +568,20 @@ public async Task> GetTransaction(TransactionH response.Result.GetProperty("execution_result").GetArrayLength() > 0) return response; await Task.Delay(10000); + if (!cancellationToken.CanBeCanceled) + return response; + + // Casper >= v2.0.0 processed deploy contains execution_info with data + if(response.Result.TryGetProperty("execution_info", out var executionInfo) && + executionInfo.ValueKind != JsonValueKind.Null) + return response; + + // Casper < v2.0.0 processed deploy contains execution_results with data + if(response.Result.TryGetProperty("execution_results", out var executionResults) && + executionResults.ValueKind == JsonValueKind.Array && + executionResults.GetArrayLength() > 0) + + await Task.Delay(4000); } throw new TaskCanceledException("GetDeploy operation canceled"); From e14849d0c18e0dc17ba951f9fa9ddaf4811fb36a Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 28 Jun 2024 15:31:30 +0200 Subject: [PATCH 043/126] modified ServerEventsClient to connect either to node version 1.x or node version 2.x SSE endpoint. Updated ICasperClient, needed for NCTL Web Explorer Signed-off-by: David Hernando --- Casper.Network.SDK/ICasperClient.cs | 21 +++++++++++++++++++ Casper.Network.SDK/SSE/ISSEClient.cs | 4 ++++ Casper.Network.SDK/SSE/ServerEventsClient.cs | 22 +++++++++++++++++--- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/Casper.Network.SDK/ICasperClient.cs b/Casper.Network.SDK/ICasperClient.cs index 985b2d4..52e00cd 100644 --- a/Casper.Network.SDK/ICasperClient.cs +++ b/Casper.Network.SDK/ICasperClient.cs @@ -28,6 +28,14 @@ public interface ICasperClient Task> GetAccountInfo(string publicKey, int blockHeight); + Task> GetEntity(IEntityIdentifier entityIdentifier, string blockHash = null); + + Task> GetEntity(IEntityIdentifier entityIdentifier, ulong blockHeight); + + Task> GetEntity(string entityAddr, string blockHash = null); + + Task> GetEntity(string entityAddr, ulong blockHeight); + Task> QueryGlobalState(string key, string stateRootHash = null, string path = null); @@ -49,11 +57,24 @@ Task> GetAccountBalance(URef purseURef, Task> GetAccountBalance(PublicKey publicKey, string stateRootHash = null); + Task> QueryBalanceDetails(IPurseIdentifier purseIdentifier, + string blockHash = null); + + Task> QueryBalanceDetails(IPurseIdentifier purseIdentifier, + ulong blockHeight); + + Task> QueryBalanceDetailsWithStateRootHash( + IPurseIdentifier purseIdentifier, string stateRootHash); + Task> PutDeploy(Deploy deploy); Task> GetDeploy(string deployHash, CancellationToken cancellationToken = default(CancellationToken)); + Task> GetTransaction(TransactionHash transactionHash, + bool finalizedApprovals = false, + CancellationToken cancellationToken = default(CancellationToken)); + Task> GetBlock(string blockHash = null); Task> GetBlock(int blockHeight); diff --git a/Casper.Network.SDK/SSE/ISSEClient.cs b/Casper.Network.SDK/SSE/ISSEClient.cs index 1bd562d..53fc7fa 100644 --- a/Casper.Network.SDK/SSE/ISSEClient.cs +++ b/Casper.Network.SDK/SSE/ISSEClient.cs @@ -9,10 +9,14 @@ void AddEventCallback(EventType eventType, string name, EventCallback cb, bool RemoveEventCallback(EventType eventType, string name); + int NodeVersion { get; set; } + void StartListening(); Task StopListening(); + bool IsRunning(); + void Wait(); } } diff --git a/Casper.Network.SDK/SSE/ServerEventsClient.cs b/Casper.Network.SDK/SSE/ServerEventsClient.cs index 04b066a..207ad49 100644 --- a/Casper.Network.SDK/SSE/ServerEventsClient.cs +++ b/Casper.Network.SDK/SSE/ServerEventsClient.cs @@ -103,6 +103,7 @@ public class ServerEventsClient : ISSEClient protected string _host; protected int _port; + protected int _nodeVersion = 1; public ServerEventsClient() { @@ -126,12 +127,19 @@ public ServerEventsClient() /// /// IP or domain name of the node. /// Event stream port. - public ServerEventsClient(string host, int port) : this() + public ServerEventsClient(string host, int port, int nodeVersion = 2) : this() { _host = host; _port = port; + _nodeVersion = nodeVersion; } + public int NodeVersion + { + get { return _nodeVersion; } + set { _nodeVersion = value; } + } + /// /// Adds an event callback method that is called for each subscribed event emitted by the node. /// @@ -233,6 +241,8 @@ public async Task StopListening() } await Task.WhenAll(tasks); + + _runningTasks.Clear(); } /// @@ -250,6 +260,11 @@ public void Wait() Task.WhenAll(tasks).Wait(); } + public bool IsRunning() + { + return _runningTasks.Count > 0; + } + /// /// Returns an instance of an HttpClient. Derived classes can override this method to get /// the client object from an HttpClientFactory, for example. @@ -276,8 +291,9 @@ private Task ListenChannelAsync(ChannelType channelType, int? startFrom, Cancell try { var uriBuilder = new UriBuilder(new Uri(client.BaseAddress + - $"events")); - + $"events" + + (_nodeVersion == 1 ? $"/{channelType.ToString().ToLowerInvariant()}" : ""))); + if (startFrom != null && startFrom != int.MaxValue) uriBuilder.Query = $"start_from={startFrom}"; else From fcb3eb03cf950a4196cc74aa16d3ce08b34efcdf Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 1 Jul 2024 11:49:37 +0200 Subject: [PATCH 044/126] refactor GetBalance / QueryBalance methods. Signed-off-by: David Hernando --- .../QueryBalanceDetailsResultTest.cs | 4 +- .../RPCResponses/QueryBalanceResultTest.cs | 21 ++++ .../TestData/query-balance-v200.json | 4 + Casper.Network.SDK/ICasperClient.cs | 22 +++- Casper.Network.SDK/JsonRpc/CasperMethods.cs | 9 +- .../JsonRpc/ResultTypes/GetBalanceResult.cs | 60 --------- .../JsonRpc/ResultTypes/QueryBalanceResult.cs | 25 ++++ Casper.Network.SDK/NetCasperClient.cs | 119 +++--------------- 8 files changed, 93 insertions(+), 171 deletions(-) create mode 100644 Casper.Network.SDK.Test/RPCResponses/QueryBalanceResultTest.cs create mode 100644 Casper.Network.SDK.Test/TestData/query-balance-v200.json create mode 100644 Casper.Network.SDK/JsonRpc/ResultTypes/QueryBalanceResult.cs diff --git a/Casper.Network.SDK.Test/RPCResponses/QueryBalanceDetailsResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/QueryBalanceDetailsResultTest.cs index f85cbb4..099062f 100644 --- a/Casper.Network.SDK.Test/RPCResponses/QueryBalanceDetailsResultTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/QueryBalanceDetailsResultTest.cs @@ -1,12 +1,10 @@ using System.IO; using Casper.Network.SDK.JsonRpc.ResultTypes; -using Casper.Network.SDK.Types; using NUnit.Framework; -using Org.BouncyCastle.Utilities.Encoders; namespace NetCasperTest.RPCResponses { - public class GetNodeStatusResultTest + public class QueryBalanceDetailsResultTest { [Test] public void GetBlockResultTest_v200() diff --git a/Casper.Network.SDK.Test/RPCResponses/QueryBalanceResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/QueryBalanceResultTest.cs new file mode 100644 index 0000000..59ba094 --- /dev/null +++ b/Casper.Network.SDK.Test/RPCResponses/QueryBalanceResultTest.cs @@ -0,0 +1,21 @@ +using System.IO; +using Casper.Network.SDK.JsonRpc.ResultTypes; +using NUnit.Framework; + +namespace NetCasperTest.RPCResponses +{ + public class GetBalanceResultTest + { + [Test] + public void GetBlockResultTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/query-balance-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + Assert.AreEqual("1576039612769", result.BalanceValue.ToString()); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/query-balance-v200.json b/Casper.Network.SDK.Test/TestData/query-balance-v200.json new file mode 100644 index 0000000..473fb81 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/query-balance-v200.json @@ -0,0 +1,4 @@ +{ + "api_version": "2.0.0", + "balance": "1576039612769" +} diff --git a/Casper.Network.SDK/ICasperClient.cs b/Casper.Network.SDK/ICasperClient.cs index 985b2d4..396e548 100644 --- a/Casper.Network.SDK/ICasperClient.cs +++ b/Casper.Network.SDK/ICasperClient.cs @@ -40,15 +40,27 @@ Task> QueryGlobalStateWithBlockHash(string k Task> QueryGlobalStateWithBlockHash(GlobalStateKey key, string blockHash, string path = null); - Task> GetAccountBalance(string purseURef, + Task> GetBalance(string purseURef, string stateRootHash = null); - Task> GetAccountBalance(URef purseURef, - string stateRootHash = null); + Task> QueryBalance(IPurseIdentifier purseIdentifier, + string blockHash = null); - Task> GetAccountBalance(PublicKey publicKey, - string stateRootHash = null); + Task> QueryBalance(IPurseIdentifier purseIdentifier, + ulong blockHeight); + + Task> QueryBalanceWithStateRootHash( + IPurseIdentifier purseIdentifier, string stateRootHash); + + Task> QueryBalanceDetails(IPurseIdentifier purseIdentifier, + string blockHash = null); + + Task> QueryBalanceDetails(IPurseIdentifier purseIdentifier, + ulong blockHeight); + Task> QueryBalanceDetailsWithStateRootHash( + IPurseIdentifier purseIdentifier, string stateRootHash); + Task> PutDeploy(Deploy deploy); Task> GetDeploy(string deployHash, diff --git a/Casper.Network.SDK/JsonRpc/CasperMethods.cs b/Casper.Network.SDK/JsonRpc/CasperMethods.cs index 1421d06..a29bda8 100644 --- a/Casper.Network.SDK/JsonRpc/CasperMethods.cs +++ b/Casper.Network.SDK/JsonRpc/CasperMethods.cs @@ -181,17 +181,20 @@ public GetBalance(string purseURef, string stateRootHash) : base("state_get_bala { this.Parameters = new Dictionary { - {"state_root_hash", stateRootHash}, - {"purse_uref", purseURef} + { "state_root_hash", stateRootHash }, + { "purse_uref", purseURef } }; } + } + public class QueryBalance : RpcMethod + { /// /// Query for balance information using a purse identifier and a state identifier /// /// The identifier to obtain the purse corresponding to balance query. /// The identifier for the state used for the query, if none is passed, the latest block will be used. - public GetBalance(IPurseIdentifier purseIdentifier, StateIdentifier stateIdentifier = null) : base("query_balance") + public QueryBalance(IPurseIdentifier purseIdentifier, StateIdentifier stateIdentifier = null) : base("query_balance") { this.Parameters = new Dictionary { diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBalanceResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBalanceResult.cs index db10152..b51cfba 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetBalanceResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetBalanceResult.cs @@ -1,16 +1,12 @@ -using System; using System.Numerics; -using System.Text.Json; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; -using Casper.Network.SDK.Utils; namespace Casper.Network.SDK.JsonRpc.ResultTypes { /// /// Result for "state_get_balance" RPC response. /// - [JsonConverter(typeof(BalanceResultConverter))] public class GetBalanceResult : RpcResult { /// @@ -26,60 +22,4 @@ public class GetBalanceResult : RpcResult [JsonPropertyName("merkle_proof")] public string MerkleProof { get; init; } } - - public class BalanceResultConverter : JsonConverter - { - public override GetBalanceResult Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - string api_version = null; - BigInteger balance = default; - string merkle_proof = null; - - reader.Read(); - - while (reader.TokenType == JsonTokenType.PropertyName) - { - var property = reader.GetString(); - reader.Read(); - switch (property) - { - case "api_version": - api_version = reader.GetString(); - reader.Read(); - break; - case "balance": - case "balance_value": - balance = BigInteger.Parse(reader.GetString()); - reader.Read(); - break; - case "merkle_proof": - merkle_proof = reader.GetString(); - reader.Read(); - break; - default: - throw new JsonException($"Unexpected property '{property}' deserializing QueryBalanceResult"); - } - } - - reader.Read(); - - return new GetBalanceResult - { - ApiVersion = api_version, - BalanceValue = balance, - MerkleProof = merkle_proof, - }; - } - - public override void Write( - Utf8JsonWriter writer, - GetBalanceResult ttlInMillis, - JsonSerializerOptions options) - { - throw new NotImplementedException(); - } - } } diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/QueryBalanceResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/QueryBalanceResult.cs new file mode 100644 index 0000000..32efe92 --- /dev/null +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/QueryBalanceResult.cs @@ -0,0 +1,25 @@ +using System.Numerics; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; + +namespace Casper.Network.SDK.JsonRpc.ResultTypes +{ + /// + /// Result for "query_balance" RPC response. + /// + public class QueryBalanceResult : RpcResult + { + /// + /// The balance value. + /// + [JsonPropertyName("balance")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger BalanceValue { get; init; } + + /// + /// The merkle proof. + /// + [JsonPropertyName("merkle_proof")] + public string MerkleProof { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index 8bfa60d..7558f73 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -265,128 +265,47 @@ public async Task> QueryGlobalStateWithBlock /// /// Purse URef formatted as a string. /// Hash of the state root. Null to get latest available. - public async Task> GetAccountBalance(string purseURef, + public async Task> GetBalance(string purseURef, string stateRootHash = null) { - if (!purseURef.StartsWith("uref-")) - { - var response = await GetAccountInfo(purseURef); - purseURef = response.Result.GetProperty("account") - .GetProperty("main_purse").GetString(); - } - - var uref = new URef(purseURef); - - var method = new GetBalance(uref, StateIdentifier.WithStateRootHash(stateRootHash)); - return await SendRpcRequestAsync(method); - } - - /// - /// Request a purse's balance from the network. - /// - /// Purse URef key. - /// Hash of the state root. Null to get latest available. - public async Task> GetAccountBalance(URef purseURef, - string stateRootHash = null) - { - var method = new GetBalance(purseURef, StateIdentifier.WithStateRootHash(stateRootHash)); + var method = new GetBalance(purseURef, stateRootHash); return await SendRpcRequestAsync(method); } /// - /// Request the balance information of an account given its account hash key. + /// Request the balance information from a PublicKey, AccountHashKey, URef or EntityAddr. /// - /// The account hash of the account to request the balance. - /// Hash of the state root. Null to get latest available. - public async Task> GetAccountBalance(AccountHashKey accountHash, - string stateRootHash = null) - { - var method = new GetBalance(accountHash, StateIdentifier.WithStateRootHash(stateRootHash)); - return await SendRpcRequestAsync(method); - } - - /// - /// Request the balance information of an account given its public key. - /// - /// The public key of the account to request the balance. - /// Hash of the state root. Null to get latest available. - public async Task> GetAccountBalance(PublicKey publicKey, - string stateRootHash = null) - { - var method = new GetBalance(publicKey, StateIdentifier.WithStateRootHash(stateRootHash)); - return await SendRpcRequestAsync(method); - } - - /// - /// Request a purse's balance from the network. - /// - /// Purse URef key. - /// Hash of the block. Null to get latest available. - public async Task> GetAccountBalanceWithBlockHash(URef purseURef, - string blockHash = null) - { - var method = new GetBalance(purseURef, StateIdentifier.WithBlockHash(blockHash)); - return await SendRpcRequestAsync(method); - } - - /// - /// Request the balance information of an account given its account hash key. - /// - /// The account hash of the account to request the balance. - /// Hash of the block. Null to get latest available. - public async Task> GetAccountBalanceWithBlockHash(AccountHashKey accountHash, - string blockHash = null) - { - var method = new GetBalance(accountHash, StateIdentifier.WithBlockHash(blockHash)); - return await SendRpcRequestAsync(method); - } - - /// - /// Request the balance information of an account given its public key. - /// - /// The public key of the account to request the balance. + /// A PublicKey, AccountHashKey, URef or EntityAddr to identify a purse. /// Hash of the block. Null to get latest available. - public async Task> GetAccountBalanceWithBlockHash(PublicKey publicKey, + public async Task> QueryBalance(IPurseIdentifier purseIdentifier, string blockHash = null) { - var method = new GetBalance(publicKey, StateIdentifier.WithBlockHash(blockHash)); - return await SendRpcRequestAsync(method); - } - - /// - /// Request a purse's balance from the network. - /// - /// Purse URef key. - /// Height of the block. - public async Task> GetAccountBalance(URef purseURef, - ulong blockHeight) - { - var method = new GetBalance(purseURef, StateIdentifier.WithBlockHeight(blockHeight)); - return await SendRpcRequestAsync(method); + var method = new QueryBalance(purseIdentifier, blockHash != null ? StateIdentifier.WithBlockHash(blockHash) : null); + return await SendRpcRequestAsync(method); } /// - /// Request the balance information of an account given its account hash key. + /// Request the balance information from a PublicKey, AccountHashKey, URef or EntityAddr. /// - /// The account hash of the account to request the balance. + /// A PublicKey, AccountHashKey, URef or EntityAddr to identify a purse. /// Height of the block. - public async Task> GetAccountBalance(AccountHashKey accountHash, + public async Task> QueryBalance(IPurseIdentifier purseIdentifier, ulong blockHeight) { - var method = new GetBalance(accountHash, StateIdentifier.WithBlockHeight(blockHeight)); - return await SendRpcRequestAsync(method); + var method = new QueryBalance(purseIdentifier, StateIdentifier.WithBlockHeight(blockHeight)); + return await SendRpcRequestAsync(method); } /// - /// Request the balance information of an account given its public key. + /// Request the balance information from a PublicKey, AccountHashKey, URef or EntityAddr. /// - /// The public key of the account to request the balance. - /// Height of the block. - public async Task> GetAccountBalance(PublicKey publicKey, - ulong blockHeight) + /// A PublicKey, AccountHashKey, URef or EntityAddr to identify a purse. + /// the state root hash. + public async Task> QueryBalanceWithStateRootHash(IPurseIdentifier purseIdentifier, + string stateRootHash) { - var method = new GetBalance(publicKey, StateIdentifier.WithBlockHeight(blockHeight)); - return await SendRpcRequestAsync(method); + var method = new QueryBalance(purseIdentifier, StateIdentifier.WithStateRootHash(stateRootHash)); + return await SendRpcRequestAsync(method); } /// From 909e39534d24a291a6eb29a4166906069e523460 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 1 Jul 2024 18:17:24 +0200 Subject: [PATCH 045/126] implement IPurseIdentifier in the AddressableEntityKey Signed-off-by: David Hernando --- .../Types/GlobalStateKey/AddressableEntityKey.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs index f9aacdf..7340038 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs @@ -39,7 +39,7 @@ public static string ToKeyPrefix(this EntityKindEnum kind) } } - public class AddressableEntityKey : GlobalStateKey, IEntityIdentifier + public class AddressableEntityKey : GlobalStateKey, IEntityIdentifier, IPurseIdentifier { public EntityKindEnum Kind { get; init; } @@ -113,5 +113,16 @@ public Dictionary GetEntityIdentifier() {"EntityAddr", Key} }; } + + /// + /// Returns a PurseIdentifier object as defined in the RPC schema for an entity address + /// + public Dictionary GetPurseIdentifier() + { + return new Dictionary + { + {"main_purse_under_entity_addr", this.ToString()} + }; + } } } From d9405a3a813bbd22203b0c7ded392e3e36e84377 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 2 Jul 2024 10:08:59 +0200 Subject: [PATCH 046/126] Added Message parsing to TransactionProcessed SSE event. Signed-off-by: David Hernando --- Casper.Network.SDK.Test/SSETypesTest.cs | 52 ++++- .../sse-transaction-processed-v200.json | 202 ++++++++++++++++++ .../SSE/TransactionProcessed.cs | 54 +++++ .../GlobalStateKey/AddressableEntityKey.cs | 57 ++++- .../Types/GlobalStateKey/MessageKey.cs | 23 +- Casper.Network.SDK/Types/Message.cs | 55 ++++- Casper.Network.SDK/Types/StoredValue.cs | 7 +- 7 files changed, 414 insertions(+), 36 deletions(-) create mode 100644 Casper.Network.SDK.Test/TestData/sse-transaction-processed-v200.json create mode 100644 Casper.Network.SDK/SSE/TransactionProcessed.cs diff --git a/Casper.Network.SDK.Test/SSETypesTest.cs b/Casper.Network.SDK.Test/SSETypesTest.cs index 8457a5f..79b410f 100644 --- a/Casper.Network.SDK.Test/SSETypesTest.cs +++ b/Casper.Network.SDK.Test/SSETypesTest.cs @@ -12,7 +12,7 @@ public void FinalitySignatureV1Test() { string testFile = TestContext.CurrentContext.TestDirectory + "/TestData/finality_signature_v156.json"; var json = File.ReadAllText(testFile); - + var doc = System.Text.Json.JsonDocument.Parse(json); json = doc.RootElement.GetProperty("FinalitySignature").GetRawText(); @@ -26,27 +26,29 @@ public void FinalitySignatureV1Test() Assert.AreEqual(0, value.BlockHeight); Assert.AreEqual(13859, value.EraId); Assert.IsNull(value.ChainNameHash); - Assert.AreEqual("01221b61b83b889898363501c7defd7baa6729989d8fce2dfffea4632017fd46cc34b88bc85fa570a6e9ada829c67b9fdaa78e8dee2f07d346bcad0010e1d3df0f", + Assert.AreEqual( + "01221b61b83b889898363501c7defd7baa6729989d8fce2dfffea4632017fd46cc34b88bc85fa570a6e9ada829c67b9fdaa78e8dee2f07d346bcad0010e1d3df0f", value.Signature.ToHexString().ToLowerInvariant()); - Assert.AreEqual("01b71b2d746681b4e0f44ef137d72ee0d42122b08ef569dc65bb0395cb624f99e5", + Assert.AreEqual("01b71b2d746681b4e0f44ef137d72ee0d42122b08ef569dc65bb0395cb624f99e5", value.PublicKey.ToString().ToLowerInvariant()); var v1 = (FinalitySignatureV1)value; - + Assert.AreEqual("d800de72aa40c6df064c714d3a6d8b6fab73f68742d8468b84efd6616bbb10bb", v1.BlockHash); Assert.AreEqual(13859, v1.EraId); - Assert.AreEqual("01221b61b83b889898363501c7defd7baa6729989d8fce2dfffea4632017fd46cc34b88bc85fa570a6e9ada829c67b9fdaa78e8dee2f07d346bcad0010e1d3df0f", + Assert.AreEqual( + "01221b61b83b889898363501c7defd7baa6729989d8fce2dfffea4632017fd46cc34b88bc85fa570a6e9ada829c67b9fdaa78e8dee2f07d346bcad0010e1d3df0f", v1.Signature.ToHexString().ToLowerInvariant()); - Assert.AreEqual("01b71b2d746681b4e0f44ef137d72ee0d42122b08ef569dc65bb0395cb624f99e5", + Assert.AreEqual("01b71b2d746681b4e0f44ef137d72ee0d42122b08ef569dc65bb0395cb624f99e5", v1.PublicKey.ToString().ToLowerInvariant()); } - + [Test] public void FinalitySignatureV2Test() { string testFile = TestContext.CurrentContext.TestDirectory + "/TestData/finality_signature_v2.json"; var json = File.ReadAllText(testFile); - + var doc = System.Text.Json.JsonDocument.Parse(json); json = doc.RootElement.GetProperty("FinalitySignature").GetRawText(); @@ -60,10 +62,40 @@ public void FinalitySignatureV2Test() Assert.AreEqual(54, value.BlockHeight); Assert.AreEqual(5, value.EraId); Assert.AreEqual("8a09603fc862b15412b60a050d71f69c57601b6da7382dd56b9a3f300822bb75", value.ChainNameHash); - Assert.AreEqual("01a12e3601b4d5c82177231910adaabf50d8a52416e756efee1694ee7534ae16fdb59a7370d564715615736074850b9255ee246c8ffa2502c167c6ef8a86b06504", + Assert.AreEqual( + "01a12e3601b4d5c82177231910adaabf50d8a52416e756efee1694ee7534ae16fdb59a7370d564715615736074850b9255ee246c8ffa2502c167c6ef8a86b06504", value.Signature.ToHexString().ToLowerInvariant()); - Assert.AreEqual("01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + Assert.AreEqual("01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", value.PublicKey.ToString().ToLowerInvariant()); } + + [Test] + public void TransactionProcessedTest() + { + string testFile = TestContext.CurrentContext.TestDirectory + + "/TestData/sse-transaction-processed-v200.json"; + var json = File.ReadAllText(testFile); + + var value = JsonSerializer.Deserialize(json); + Assert.IsNotNull(value); + + Assert.AreEqual("9749ce1dc5dbd0d9a611088f934fd81c2c8429dbab0a3a7d281359be0a92d29a", + value.TransactionHash.Version1); + Assert.AreEqual("01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", + value.InitiatorAddr.PublicKey.ToString().ToLower()); + Assert.AreEqual("b3fc60731e800dedfc60fcb1d85b91a93cf688237e53d2f8920e4fad4ab047ee", value.BlockHash); + Assert.IsTrue(value.ExecutionResult.Effect.Count > 0); + + Assert.AreEqual(1, value.Messages.Count); + var message = value.Messages[0]; + Assert.AreEqual("entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed", + message.AddressableEntity.ToString()); + Assert.IsNotEmpty(message.MessagePayload.String); + Assert.IsNull(message.MessagePayload.Bytes); + Assert.AreEqual("events", message.TopicName); + Assert.AreEqual("5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", message.TopicNameHash); + Assert.AreEqual(1, message.TopicIndex); + Assert.AreEqual(2, message.BlockIndex); + } } } \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/sse-transaction-processed-v200.json b/Casper.Network.SDK.Test/TestData/sse-transaction-processed-v200.json new file mode 100644 index 0000000..96a5b0d --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/sse-transaction-processed-v200.json @@ -0,0 +1,202 @@ +{ + "transaction_hash": { + "Version1": "9749ce1dc5dbd0d9a611088f934fd81c2c8429dbab0a3a7d281359be0a92d29a" + }, + "initiator_addr": { + "PublicKey": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a" + }, + "timestamp": "2024-07-01T15:24:47.381Z", + "ttl": "30m", + "block_hash": "b3fc60731e800dedfc60fcb1d85b91a93cf688237e53d2f8920e4fad4ab047ee", + "execution_result": { + "Version2": { + "initiator": { + "PublicKey": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a" + }, + "error_message": null, + "limit": "100000000000", + "consumed": "520224703", + "cost": "100000000000", + "payment": [], + "transfers": [], + "size_estimate": 366, + "effects": [ + { + "key": "balance-hold-01a9f3707fb752513760d089f4625a8727ba1e8b44880a8180e3780c29003742975c0ee66e90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "0500e8764817", + "parsed": "100000000000" + } + } + } + }, + { + "key": "package-257b05da64d656592cde44bf3aa9f70f97b377b6342e016234d90aaaaaf0cfc5", + "kind": "Identity" + }, + { + "key": "entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed", + "kind": "Identity" + }, + { + "key": "package-257b05da64d656592cde44bf3aa9f70f97b377b6342e016234d90aaaaaf0cfc5", + "kind": "Identity" + }, + { + "key": "entry-point-v1-entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed-3820ce25e54df0470fb738e3e0f63ee50b2719cf2680da2bdb579e21aebc8f63", + "kind": "Identity" + }, + { + "key": "byte-code-v1-wasm-9e011083366d6dd0c32fafdf2b2ed5fa021b9c1c001af4c25b76715d6ef026af", + "kind": "Identity" + }, + { + "key": "dictionary-83e84f07db6e29c87ca51d44ac058f7ff61e0c1050eaf16110237b848048133f", + "kind": "Identity" + }, + { + "key": "dictionary-2d3fe9773442ce06ecb65e86a4a92d6818cb51cd6021218612a35250a7320298", + "kind": "Identity" + }, + { + "key": "dictionary-83e84f07db6e29c87ca51d44ac058f7ff61e0c1050eaf16110237b848048133f", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "0800000007aebcc5a47e8d0307200000002d5926cca6ebf4fd7d3555dca958f1fa3c6152418f89908515c062b61e7b665b30000000455147666f667749434e4f6c756571664f76544b6649773256566150337a654e69763334702b5675574b752f31413d3d", + "parsed": null + } + } + } + }, + { + "key": "dictionary-2d3fe9773442ce06ecb65e86a4a92d6818cb51cd6021218612a35250a7320298", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "030000000252c307200000002d5926cca6ebf4fd7d3555dca958f1fa3c6152418f89908515c062b61e7b665b300000004551482f716e4d7a46434865517631786f6f676b34574271484b663664552b52395148777a31594258334b457a513d3d", + "parsed": null + } + } + } + }, + { + "key": "uref-9561215a982d288e4d20426b9d45605b1642c7d0fe54c9f8632eb087b0a3dc7a-000", + "kind": "Identity" + }, + { + "key": "message-topic-entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "kind": "Identity" + }, + { + "key": "block-message-count-00000000000000000000000000000000000000000000000000000000000000", + "kind": "Identity" + }, + { + "key": "message-entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-0", + "kind": { + "Write": { + "Message": "message-checksum-4fa4135e65967751f007064a7cbf6c17d27f7189cc97b7b0f484445c72ecbd6f" + } + } + }, + { + "key": "message-topic-entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "kind": { + "Write": { + "MessageTopic": { + "message_count": 1, + "blocktime": 1719847489116 + } + } + } + }, + { + "key": "block-message-count-00000000000000000000000000000000000000000000000000000000000000", + "kind": { + "Write": { + "CLValue": { + "cl_type": { + "Tuple2": [ + "U64", + "U64" + ] + }, + "bytes": "5c0ee66e900100000100000000000000", + "parsed": [ + 1719847489116, + 1 + ] + } + } + } + }, + { + "key": "balance-hold-01a9f3707fb752513760d089f4625a8727ba1e8b44880a8180e3780c29003742975c0ee66e90010000", + "kind": { + "Prune": "balance-hold-01a9f3707fb752513760d089f4625a8727ba1e8b44880a8180e3780c29003742975c0ee66e90010000" + } + }, + { + "key": "balance-hold-00a9f3707fb752513760d089f4625a8727ba1e8b44880a8180e3780c29003742975c0ee66e90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "0500e8764817", + "parsed": "100000000000" + } + } + } + }, + { + "key": "entity-system-5a28db419f9dfc65ad39f6c221a7801ff16ee15b6140a6ae7eb96903544b4928", + "kind": "Identity" + }, + { + "key": "entity-system-3d4d648d49f1840fdbf82cb41dbec8b64b164ed17dfe0fea0d444bdd44f688d3", + "kind": "Identity" + }, + { + "key": "entity-system-ba7e4fcf24bdc36fc932881f4989e7df2b542e8e58700765a8155903b9ced06b", + "kind": "Identity" + }, + { + "key": "bid-addr-01dcd0c38c46c9d5c7083aa1a46b430e8e460f97f8f0bf8444776ac925187acfcc", + "kind": "Identity" + }, + { + "key": "bid-addr-04dcd0c38c46c9d5c7083aa1a46b430e8e460f97f8f0bf8444776ac925187acfcc0c00000000000000", + "kind": { + "Write": { + "BidKind": { + "Credit": { + "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "era_id": 12, + "amount": "100000000000" + } + } + } + } + } + ] + } + }, + "messages": [ + { + "entity_hash": "entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed", + "message": { + "String": "Transfer(Transfer { sender: Key::AddressableEntity(account-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4), recipient: Key::AddressableEntity(account-ffaa73331421de42fd71a28824e1606a1ca7fa754f91f501f0cf56015f7284cd), amount: 12502 })" + }, + "topic_name": "events", + "topic_name_hash": "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "topic_index": 1, + "block_index": 2 + } + ] +} \ No newline at end of file diff --git a/Casper.Network.SDK/SSE/TransactionProcessed.cs b/Casper.Network.SDK/SSE/TransactionProcessed.cs new file mode 100644 index 0000000..0d1df2b --- /dev/null +++ b/Casper.Network.SDK/SSE/TransactionProcessed.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; +using Casper.Network.SDK.Types; + +namespace Casper.Network.SDK.SSE +{ + public class TransactionProcessed + { + /// + /// Versioned transaction hash. + /// + [JsonPropertyName("transaction_hash")] + public TransactionHash TransactionHash { get; init; } + + /// + /// The address of the initiator of a transaction. + /// + [JsonPropertyName("initiator_addr")] + public InitiatorAddr InitiatorAddr { get; set; } + + /// + /// The timestamp in which the Transaction was built. + /// + [JsonPropertyName("timestamp")] + public string Timestamp { get; init; } + + /// + /// The time-to-live of the Transaction. + /// + [JsonPropertyName("ttl")] + [JsonConverter(typeof(HumanizeTTLConverter))] + public ulong Ttl { get; set; } + + /// + /// The hash of the block containing this Transaction. + /// + [JsonPropertyName("block_hash")] + public string BlockHash { get; init; } + + /// + /// Versioned execution result. + /// + [JsonPropertyName("execution_result")] + [JsonConverter(typeof(ExecutionResult.ExecutionResultConverter))] + public ExecutionResult ExecutionResult { get; init; } + + /// + /// List of messages emitted in the transaction execution + /// + [JsonPropertyName("messages")] + public List Messages { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs index 88255dc..d8bf72f 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; using Casper.Network.SDK.Utils; using Org.BouncyCastle.Utilities.Encoders; @@ -39,7 +41,7 @@ public static string ToKeyPrefix(this EntityKindEnum kind) } } - public class AddressableEntityKey : GlobalStateKey, IEntityIdentifier + public class AddressableEntityKey : GlobalStateKey, IEntityIdentifier, IPurseIdentifier { public EntityKindEnum Kind { get; init; } @@ -87,6 +89,22 @@ public AddressableEntityKey(BinaryReader reader) : base(null) Key = Kind.ToKeyPrefix() + Hex.ToHexString(addr); } + protected override byte[] _GetRawBytesFromKey(string key) + { + return this.GetBytes(); + } + + public override byte[] GetBytes() + { + var key = Key.Substring(Key.LastIndexOf('-')+1); + var rawBytes = Hex.Decode(key); + var ms = new MemoryStream(); + ms.WriteByte((byte)this.Kind); + ms.Write(rawBytes); + + return ms.ToArray(); + } + /// /// Returns an EntityIdentifier object as defined in the RPC schema for an account hash key. /// @@ -97,5 +115,42 @@ public Dictionary GetEntityIdentifier() {"EntityAddr", Key} }; } + + /// + /// Returns a PurseIdentifier object as defined in the RPC schema for an entity address + /// + public Dictionary GetPurseIdentifier() + { + return new Dictionary + { + {"main_purse_under_entity_addr", this.ToString()} + }; + } + + public class AddressableEntityKeyConverter : JsonConverter + { + public override AddressableEntityKey Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + return new AddressableEntityKey(reader.GetString()); + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + AddressableEntityKey addressableEntity, + JsonSerializerOptions options) + { + writer.WriteStringValue(addressableEntity.Key); + } + } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs index 077696c..2e14080 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs @@ -7,35 +7,22 @@ namespace Casper.Network.SDK.Types { public class MessageKey : GlobalStateKey { - private const string MESSAGE_PREFIX = "message-"; + private const string KEY_PREFIX = "message-"; private const string TOPIC_PREFIX = "topic-"; - private static readonly string CONTRACT_TOPIC_KEYPREFIX = "message-topic-entity-contract-"; - private static readonly string CONTRACT_MSG_KEYPREFIX = "message-entity-contract-"; - public AddressableEntityKey AddressableEntity { get; init; } public string TopicHash { get; init; } public UInt32? Index { get; init; } - private static string GetPrefix(string key) - { - if (key.StartsWith(CONTRACT_TOPIC_KEYPREFIX)) - return CONTRACT_TOPIC_KEYPREFIX; - if (key.StartsWith(CONTRACT_MSG_KEYPREFIX)) - return CONTRACT_MSG_KEYPREFIX; - - throw new Exception("Unexpected key prefix in ByteCodeKey: " + key); - } - public MessageKey(string key) : base(key) { KeyIdentifier = KeyIdentifier.Message; - if (!key.StartsWith(MESSAGE_PREFIX)) - throw new ArgumentException($"Key not valid. It should start with '{MESSAGE_PREFIX}'."); - key = key.Substring(MESSAGE_PREFIX.Length); + if (!key.StartsWith(KEY_PREFIX)) + throw new ArgumentException($"Key not valid. It should start with '{KEY_PREFIX}'."); + key = key.Substring(KEY_PREFIX.Length); if (key.StartsWith(TOPIC_PREFIX)) { @@ -78,7 +65,7 @@ public MessageKey(byte[] key) : base(null) if (optionIndexTag == 0x01) Index = reader.ReadUInt32(); - Key = MESSAGE_PREFIX + + Key = KEY_PREFIX + (Index.HasValue ? "" : TOPIC_PREFIX) + AddressableEntity.ToString() + "-" + TopicHash + diff --git a/Casper.Network.SDK/Types/Message.cs b/Casper.Network.SDK/Types/Message.cs index 3d58810..bb2c3d4 100644 --- a/Casper.Network.SDK/Types/Message.cs +++ b/Casper.Network.SDK/Types/Message.cs @@ -21,8 +21,61 @@ public class MessageTopicSummary public UInt32 MessageCount { get; init; } } - public class MessageChecksum + /// + /// The payload of a message. + /// + public class MessagePayload { + /// + /// Human readable string message. + /// + public string String { get; init; } + /// + /// Message represented as raw bytes. + /// + public string Bytes { get; init; } + } + + /// + /// Message that was emitted by an addressable entity during execution. + /// + public class Message + { + /// + /// The identity of the entity that produced the message. + /// + [JsonPropertyName("entity_hash")] + [JsonConverter(typeof(AddressableEntityKey.AddressableEntityKeyConverter))] + public AddressableEntityKey AddressableEntity { get; init; } + + /// + /// The payload of the message. + /// + [JsonPropertyName("message")] + public MessagePayload MessagePayload { get; init; } + /// + /// The name of the topic on which the message was emitted on. + /// + [JsonPropertyName("topic_name")] + public string TopicName { get; init; } + + /// + /// The hash of the name of the topic. + /// + [JsonPropertyName("topic_name_hash")] + public string TopicNameHash { get; init; } + + /// + /// Message index in the topic. + /// + [JsonPropertyName("topic_index")] + public uint TopicIndex { get; init; } + + /// + /// Message index in the block. + /// + [JsonPropertyName("block_index")] + public ulong BlockIndex { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/StoredValue.cs b/Casper.Network.SDK/Types/StoredValue.cs index 47e33bd..155213a 100644 --- a/Casper.Network.SDK/Types/StoredValue.cs +++ b/Casper.Network.SDK/Types/StoredValue.cs @@ -39,11 +39,6 @@ public class StoredValue /// public AddressableEntity AddressableEntity { get; init; } - /// - /// Stores a BidKind. - /// - // public BidKind BidKind { get; init; } - /// /// Stores a package. /// @@ -60,7 +55,7 @@ public class StoredValue public MessageTopicSummary MessageTopic { get; init; } /// - /// Stores a message digest. + /// Stores a message checksum. /// public string Message { get; init; } From b451f01c1109172f35d37840598e31ec595080ae Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 3 Jul 2024 15:13:16 +0200 Subject: [PATCH 047/126] implemented IPurseIdentifier interface (missed on rebase). Signed-off-by: David Hernando --- Casper.Network.SDK/Types/AddressableEntity.cs | 7 +- .../GlobalStateKey/AddressableEntityKey.cs | 78 +++++++++++++------ 2 files changed, 58 insertions(+), 27 deletions(-) diff --git a/Casper.Network.SDK/Types/AddressableEntity.cs b/Casper.Network.SDK/Types/AddressableEntity.cs index c80e8a4..7c96397 100644 --- a/Casper.Network.SDK/Types/AddressableEntity.cs +++ b/Casper.Network.SDK/Types/AddressableEntity.cs @@ -28,7 +28,7 @@ public enum SystemEntityType Auction, } - + [JsonConverter(typeof(EntityKindConverter))] public class EntityKind { @@ -67,6 +67,7 @@ public override EntityKind Read( SmartContract = true, }; } + if (reader.TokenType == JsonTokenType.StartObject) { reader.Read(); @@ -90,6 +91,7 @@ public override EntityKind Read( }; break; } + reader.Read(); if (entity != null) return entity; @@ -182,5 +184,4 @@ public class AddressableEntity [JsonPropertyName("message_topics")] public List MessageTopics { get; init; } } -} - +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs index 219d575..fcd4ba4 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs @@ -1,26 +1,31 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; using Casper.Network.SDK.Utils; using Org.BouncyCastle.Utilities.Encoders; namespace Casper.Network.SDK.Types { - public enum EntityKindEnum { + public enum EntityKindEnum + { /// /// Package associated with a native contract implementation. /// System = 0, + /// /// Package associated with an Account hash. /// Account = 1, + /// /// Package associated with Wasm stored on chain. /// Contract = 2, } - + public static class EntityKinEnumExtensions { public static string ToKeyPrefix(this EntityKindEnum kind) @@ -38,11 +43,11 @@ public static string ToKeyPrefix(this EntityKindEnum kind) } } } - - public class AddressableEntityKey : GlobalStateKey, IEntityIdentifier + + public class AddressableEntityKey : GlobalStateKey, IEntityIdentifier, IPurseIdentifier { public EntityKindEnum Kind { get; init; } - + private static string GetPrefix(string key) { if (key.StartsWith(EntityKindEnum.System.ToKeyPrefix())) @@ -53,8 +58,8 @@ private static string GetPrefix(string key) return EntityKindEnum.Contract.ToKeyPrefix(); throw new Exception("Unexpected key prefix in NamedKeyKey: " + key); - } + public AddressableEntityKey(string key) : base(key, GetPrefix(key)) { KeyIdentifier = KeyIdentifier.AddressableEntity; @@ -67,7 +72,7 @@ public AddressableEntityKey(string key) : base(key, GetPrefix(key)) Kind = EntityKindEnum.Contract; } - public AddressableEntityKey(byte[] key) : this( + public AddressableEntityKey(byte[] key) : this( key[0] == (byte)EntityKindEnum.System ? EntityKindEnum.System.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) : (key[0] == (byte)EntityKindEnum.Account @@ -86,27 +91,15 @@ public AddressableEntityKey(BinaryReader reader) : base(null) Kind = (EntityKindEnum)tag; Key = Kind.ToKeyPrefix() + Hex.ToHexString(addr); } - - /// - /// Returns an EntityIdentifier object as defined in the RPC schema for an account hash key. - /// - public Dictionary GetEntityIdentifier() - { - return new Dictionary - { - {"EntityAddr", Key} - }; - } - - + protected override byte[] _GetRawBytesFromKey(string key) { return this.GetBytes(); } - + public override byte[] GetBytes() { - var key = Key.Substring(Key.LastIndexOf('-')+1); + var key = Key.Substring(Key.LastIndexOf('-') + 1); var rawBytes = Hex.Decode(key); var ms = new MemoryStream(); ms.WriteByte((byte)this.Kind); @@ -114,7 +107,18 @@ public override byte[] GetBytes() return ms.ToArray(); } - + + /// + /// Returns an EntityIdentifier object as defined in the RPC schema for an account hash key. + /// + public Dictionary GetEntityIdentifier() + { + return new Dictionary + { + { "EntityAddr", Key } + }; + } + /// /// Returns a PurseIdentifier object as defined in the RPC schema for an entity address /// @@ -122,8 +126,34 @@ public Dictionary GetPurseIdentifier() { return new Dictionary { - {"main_purse_under_entity_addr", this.ToString()} + { "main_purse_under_entity_addr", this.ToString() } }; } + + public class AddressableEntityKeyConverter : JsonConverter + { + public override AddressableEntityKey Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + return new AddressableEntityKey(reader.GetString()); + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + AddressableEntityKey addressableEntity, + JsonSerializerOptions options) + { + writer.WriteStringValue(addressableEntity.Key); + } + } } } \ No newline at end of file From 6870c75da7fbfa6be37587276ceab638bba4bbb3 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Sun, 7 Jul 2024 10:53:34 +0200 Subject: [PATCH 048/126] bugfix parsing AddressableEntity for SmartContract Added unit testing Signed-off-by: David Hernando --- .../RPCResponses/GetEntityResultTest.cs | 66 +++ .../TestData/get-entity-account-v200.json | 31 ++ .../TestData/get-entity-contract-v200.json | 385 ++++++++++++++++++ Casper.Network.SDK/Types/AddressableEntity.cs | 33 +- 4 files changed, 504 insertions(+), 11 deletions(-) create mode 100644 Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs create mode 100644 Casper.Network.SDK.Test/TestData/get-entity-account-v200.json create mode 100644 Casper.Network.SDK.Test/TestData/get-entity-contract-v200.json diff --git a/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs new file mode 100644 index 0000000..56c0018 --- /dev/null +++ b/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs @@ -0,0 +1,66 @@ +using System.IO; +using Casper.Network.SDK.JsonRpc.ResultTypes; +using Casper.Network.SDK.Types; +using NUnit.Framework; + +namespace NetCasperTest.RPCResponses +{ + public class GetEntityResultTest + { + [Test] + public void GetEntityAccountTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-entity-account-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + + AssertExtensions.IsValidHex(result.MerkleProof); + + Assert.IsNotNull(result.Entity.Package); + Assert.IsNotNull(result.Entity.MainPurse); + Assert.AreEqual("byte-code-0000000000000000000000000000000000000000000000000000000000000000", result.Entity.ByteCodeHash); + Assert.IsNotEmpty(result.Entity.EntityKind.Account.ToHexString()); + Assert.AreEqual(1, result.Entity.ActionThresholds.KeyManagement); + Assert.AreEqual(1, result.Entity.ActionThresholds.Deployment); + Assert.AreEqual(1, result.Entity.ActionThresholds.UpgradeManagement); + Assert.AreEqual(0, result.NamedKeys.Count); + Assert.AreEqual(0, result.EntryPoints.Count); + } + + [Test] + public void GetEntityContractTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-entity-contract-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + + AssertExtensions.IsValidHex(result.MerkleProof); + + Assert.IsNotNull(result.Entity.Package); + Assert.IsNotNull(result.Entity.MainPurse); + Assert.AreEqual("byte-code-85def61e3ee02e10a1e845cfb8e8b2d9640a18f605333158027a24ed8569d895", result.Entity.ByteCodeHash); + Assert.AreEqual(TransactionRuntime.VmCasperV1, result.Entity.EntityKind.SmartContract); + Assert.AreEqual(1, result.Entity.ActionThresholds.KeyManagement); + Assert.AreEqual(1, result.Entity.ActionThresholds.Deployment); + Assert.AreEqual(1, result.Entity.ActionThresholds.UpgradeManagement); + Assert.AreEqual(11, result.NamedKeys.Count); + Assert.AreEqual("balances", result.NamedKeys[1].Name); + Assert.AreEqual("uref-96cd0453fb2e1d063c9438c158c0d804d0121a96f3423046150ee355cfadefb6-007", result.NamedKeys[1].Key.ToString().ToLower()); + + Assert.IsTrue(result.EntryPoints.Count > 0); + Assert.AreEqual("allowance", result.EntryPoints[1].Name); + Assert.AreEqual(2, result.EntryPoints[1].Args.Count); + Assert.AreEqual("spender", result.EntryPoints[1].Args[1].Name); + Assert.AreEqual(CLType.Key, result.EntryPoints[1].Args[1].CLType); + Assert.AreEqual(CLType.U256, result.EntryPoints[1].Ret); + Assert.IsTrue(result.EntryPoints[1].Access.IsPublic); + Assert.AreEqual(EntryPointType.Called, result.EntryPoints[1].EntryPointType); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/get-entity-account-v200.json b/Casper.Network.SDK.Test/TestData/get-entity-account-v200.json new file mode 100644 index 0000000..3bc214e --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-entity-account-v200.json @@ -0,0 +1,31 @@ +{ + "api_version": "2.0.0", + "entity": { + "AddressableEntity": { + "entity": { + "protocol_version": "2.0.0", + "entity_kind": { + "Account": "account-hash-65a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b1566213" + }, + "package_hash": "package-8117d677f8d08045b0db10c9efe309a801c39c3c41e86bc801883c6343fbe318", + "byte_code_hash": "byte-code-0000000000000000000000000000000000000000000000000000000000000000", + "main_purse": "uref-7decb5dcf734bace39f398bc19a3f371ac5ffe68c23f7fa8c2abaf7ae2edd1ad-007", + "associated_keys": [ + { + "account_hash": "account-hash-65a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b1566213", + "weight": 1 + } + ], + "action_thresholds": { + "deployment": 1, + "upgrade_management": 1, + "key_management": 1 + }, + "message_topics": [] + }, + "named_keys": [], + "entry_points": [] + } + }, + "merkle_proof": "01000000110165a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b15662130d8117d677f8d08045b0db10c9efe309a801c39c3c41e86bc801883c6343fbe31800000000000000000000000000000000000000000000000000000000000000000200000000000000000000007decb5dcf734bace39f398bc19a3f371ac5ffe68c23f7fa8c2abaf7ae2edd1ad070100000065a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b156621301010101000000000165a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b1566213030000000065140000001c004140e396af85425c4d13c428fff8c94a6f7bb5dec7fd618b4e23b3b58369721822007ab457a5e6805d0fdeb823c09ab40dff1d4941951ab0b2645861aeb8e4d6b2d02b00f0f334d6a39190dbc16682ec95db4553df44d11558952b172106082bac5e0aa52f00b7dacae2754a0970a96016fa199d5ad254701873fee1cb50436cd2288b5b83a13d01c02bcc20002435fa0760dfcae0f0b3d65909f0907169ef07fc33ceee4a93ff813e00379d62bdd28b3cc340a34484ad14325f06a21b3c34d15b45ed1b5ae23cdc70e15200f060fe324c53b5e1746c06d158b1919596c3ce71a5fe3b9ff115e137415f23d75300c1603481f9cd5dfa0d86f68321cc1cba0baf160efa55e19de1ca3516468617bc5600d31eb794b67f631b10b1f5372991c4ea7367fc05ccc8e4b2bdf147d1b353f7c3610095fc5e8e2f88cf31f67b30c74e507cbfbc3370750d98834204cca8d5f43e1b4a6b0032d4fcc51712e24553680440cb511aa1a91a1fa554c9135926eb3566931e2ace87008b6dd1ababcce7cc20691e6c0e87fef231b7c9c342a43ab7b2b935b39dd4848f8800c53304e96293c383d723e2d1ba7a2b0a3f6df67643f5c45363d19a88d561163f9f0017d4c50c86f70a2ea288b1deafe78eb99986a312e750f1de6c3d75daefb83d1dc20079f01d2ea83a6ea636b1c24e9ddc7ed3487a96c5f829ffc64a2a8f8f1dd00855cd001f40c6887abe655291469455b0d84c7cd58b74d896ed63e2b58d488906d054d8db00fabbb7ee9e93dcb60b3d4f70ab3c3fde9172dcdbf9ca90bd686b2632e73485afdc00e16ac8017d72f86f6b2c9fa52d6e177efa53c89c9d734ab1c64206e6005fd2eae200a13d5697094dbcef9b9bea3a8a59f0ce57a48445ade810658f7a127eb4f9e31dff00d60902a31f311443a0490c83cc48591c64da565901940683bc4cd43fea3a8d070001010000000001329fa50131086d2de5e18cdb8487b1e27a2e04ce7a574f185d22862019271fa100110d000000000106797c4406a0a246f352ba5592920ed0b3e214c688e080962ca74d36d7a1a99002012ec2b919aac0633305c4259420ce41a8b3a530e1b7dbbdce3c60c72cd1968c9506011ff8296cf60a3a7469992155c753f1dcb063a1db770cb5fe4e4f591d5ad421dd0a0029b220b5550023f51a78e121017aebf0089c690ab74adff840c31392ce2a59370b00a4c3b00132e152ef5fe45d3a7c56254b8b1651565b7c16e34a7b0abf6969ae820d0010fa1f5eb43fa8f8d7d286d8e5394ce7362095bd049788c3f8eb82f997e675240e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f013853ed3060a42737482e66540def898bdbf8534e7b24e9999a5af9fe6671ba0e100159f9269ebe85ad359217e196d018cacb941a04bb7f8e251f0c73435fc83c277c1200693fff58318fbd94674eb111fbc49ea8ab66ece1cd614b0008d387f228d778ee14015a38f15c34cc79868b4445af51125e24c100d0cf719b1eef5202458d74c146751500dd70cbafab73cb6edfb05345ed175bd858f2985199a9fa113e6d7710facf8bed17012e0cdc4b06694b367a198ff7f1676e8fd104c958d902b2e27b7117dd1572e234" +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/get-entity-contract-v200.json b/Casper.Network.SDK.Test/TestData/get-entity-contract-v200.json new file mode 100644 index 0000000..2420021 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-entity-contract-v200.json @@ -0,0 +1,385 @@ +{ + "api_version": "2.0.0", + "entity": { + "AddressableEntity": { + "entity": { + "protocol_version": "2.0.0", + "entity_kind": { + "SmartContract": "VmCasperV1" + }, + "package_hash": "package-7a25b7c52f6c69b8d229ae50c821c5edf4e0c2bd3ac73ec52386ed42440ce5c6", + "byte_code_hash": "byte-code-85def61e3ee02e10a1e845cfb8e8b2d9640a18f605333158027a24ed8569d895", + "main_purse": "uref-d3df027aba457c3e0a657148427f90c5a6fcdbe33b0c841ee91aacd63838cbba-007", + "associated_keys": [ + { + "account_hash": "account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4", + "weight": 1 + } + ], + "action_thresholds": { + "deployment": 1, + "upgrade_management": 1, + "key_management": 1 + }, + "message_topics": [ + { + "topic_name": "events", + "topic_name_hash": "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1" + } + ] + }, + "named_keys": [ + { + "name": "allowances", + "key": "uref-2573b5cca61e31f7b602f135a9abbe096367f5c07f593d006ba10134d3698aa4-007" + }, + { + "name": "balances", + "key": "uref-96cd0453fb2e1d063c9438c158c0d804d0121a96f3423046150ee355cfadefb6-007" + }, + { + "name": "contract_hash", + "key": "entity-contract-9bd4c534c8055bc82e44a9f8c02b485008ee28108f79c8556b57dc9f5a8ef63d" + }, + { + "name": "decimals", + "key": "uref-da8081ab755640837c90d9088579f9d7e29c09c3ba0295f59b9ee166ba0bf374-007" + }, + { + "name": "enable_mint_burn", + "key": "uref-cc1accfee71597d884d2e22f7cb166f53cdf001e568a6d32ed74877d80472fca-007" + }, + { + "name": "events_mode", + "key": "uref-c537df8ce12730db7a9b74f5338b2121751d5813c65e648b7bba9a6223f5353c-007" + }, + { + "name": "name", + "key": "uref-bd0fc79a60c201f454f6840e3ed2a9f81f1ea55a7d5f59b08df5f0450b5dc9e6-007" + }, + { + "name": "package_hash", + "key": "package-7a25b7c52f6c69b8d229ae50c821c5edf4e0c2bd3ac73ec52386ed42440ce5c6" + }, + { + "name": "security_badges", + "key": "uref-f0ac8d1883c4be33bf902b44f1227b6f1fccfcb9bfa491eb13ab2d18ffeeca5e-007" + }, + { + "name": "symbol", + "key": "uref-5dde41a496dd0b1c55b44ca8fbeeb4d17669a019067533ec0ace3571c9b82b8b-007" + }, + { + "name": "total_supply", + "key": "uref-007bc04abf7d45a8e2748373899ead7691fe5349d9bcd5d95740d602a9245c4b-007" + } + ], + "entry_points": [ + { + "V1CasperVm": { + "name": "migrate_user_allowance_keys", + "args": [ + { + "name": "events", + "cl_type": "Bool" + }, + { + "name": "revert", + "cl_type": "Bool" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "allowance", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "spender", + "cl_type": "Key" + } + ], + "ret": "U256", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "migrate_user_balance_keys", + "args": [ + { + "name": "events", + "cl_type": "Bool" + }, + { + "name": "revert", + "cl_type": "Bool" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "mint", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "transfer", + "args": [ + { + "name": "recipient", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "transfer_from", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "recipient", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "approve", + "args": [ + { + "name": "spender", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "init", + "args": [], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "name", + "args": [], + "ret": "String", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "burn", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "change_security", + "args": [], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "increase_allowance", + "args": [ + { + "name": "spender", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "decrease_allowance", + "args": [ + { + "name": "spender", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "change_events_mode", + "args": [ + { + "name": "events_mode", + "cl_type": "U8" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "decimals", + "args": [], + "ret": "U8", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "symbol", + "args": [], + "ret": "String", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "total_supply", + "args": [], + "ret": "U256", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "migrate_sec_keys", + "args": [ + { + "name": "events", + "cl_type": "Bool" + }, + { + "name": "revert", + "cl_type": "Bool" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "balance_of", + "args": [ + { + "name": "address", + "cl_type": "Key" + } + ], + "ret": "U256", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + ] + } + }, + "merkle_proof": "0100000011029bd4c534c8055bc82e44a9f8c02b485008ee28108f79c8556b57dc9f5a8ef63d0d7a25b7c52f6c69b8d229ae50c821c5edf4e0c2bd3ac73ec52386ed42440ce5c685def61e3ee02e10a1e845cfb8e8b2d9640a18f605333158027a24ed8569d895020000000000000000000000d3df027aba457c3e0a657148427f90c5a6fcdbe33b0c841ee91aacd63838cbba07010000009fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd40101010101000000060000006576656e74735721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e10200020000000002020000000001329fa50131086d2de5e18cdb8487b1e27a2e04ce7a574f185d22862019271fa1010109b9c1523ad187123214b99094869646952ecf4f56f83c168adb43c690f8c7090011100000000001e9602fcc1c2dd571ad440b6157fb44ae056257301ae8ebbe64ed156289a18ed2020161620e026b0fb697da2be845771ddadeaa555a8c421b5c06f1c2209f582cda8c060166119f72425746e2133a0a8d67dfd34cd0d89db2e86e37a112c5089bf5b9524e09011d429bda3bf002784d0ea8ba8cd94ca26534c2102a088daeece5f28608a6ad240a0029b220b5550023f51a78e121017aebf0089c690ab74adff840c31392ce2a59370b00a6503cbee1ef52651ad311cd7d32d727d1c5349afa428b0109ae30e0e69aed2f0d0010fa1f5eb43fa8f8d7d286d8e5394ce7362095bd049788c3f8eb82f997e675240e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f015ba4f65f5b54cab5eab7171a7218f09c4cb20cf3a5127b7491ff9230b1ecf2ea10016997f28e86b4a18d9f74c494e44cc58a3aecec712e5f332917b6ec751f7a42711201dc991a88ba67859da4efb3d13273933b7d6e2e1cd3826e81a966b967c8cf54ee1301cfce4655aaa43c05dc69a3ebb887808e9d04b01d9c4bff5913f80c1136b83ffd1401c0956fb9a4815604a8e12528088273a2bdc7961cdc7480a6fa1c54bad136fb501501f912293449cf99294a99e4d774b1c2f8fb7f8760cd9c5625a60de980de7c3e381601483cc5eb620c842e555ffa3b69feff61910c79740010b80f8d30d6bc73d6b06417012e40a254b1d0a5136f9713b4af327548efed7ff2d2177446f14c57c103b4c4b9" +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/AddressableEntity.cs b/Casper.Network.SDK/Types/AddressableEntity.cs index 7c96397..41f761b 100644 --- a/Casper.Network.SDK/Types/AddressableEntity.cs +++ b/Casper.Network.SDK/Types/AddressableEntity.cs @@ -5,6 +5,19 @@ namespace Casper.Network.SDK.Types { + public enum TransactionRuntime + { + /// + /// The Casper Version 1 Virtual Machine. + /// + VmCasperV1, + + /// + /// The Casper Version 2 Virtual Machine. + /// + VmCasperV2, + } + public enum SystemEntityType { /// @@ -47,7 +60,7 @@ public class EntityKind /// /// Packages associated with Wasm stored on chain. /// - public bool? SmartContract { get; init; } + public TransactionRuntime? SmartContract { get; init; } /// /// Json converter class to serialize/deserialize a Block to/from Json @@ -59,15 +72,6 @@ public override EntityKind Read( Type typeToConvert, JsonSerializerOptions options) { - if (reader.TokenType == JsonTokenType.String && - reader.GetString().Equals("SmartContract", StringComparison.InvariantCultureIgnoreCase)) - { - return new EntityKind() - { - SmartContract = true, - }; - } - if (reader.TokenType == JsonTokenType.StartObject) { reader.Read(); @@ -84,6 +88,12 @@ public override EntityKind Read( Account = new AccountHashKey(reader.GetString()), }; break; + case "SmartContract": + entity = new EntityKind() + { + SmartContract = EnumCompat.Parse(reader.GetString()), + }; + break; case "System": entity = new EntityKind() { @@ -147,7 +157,8 @@ public class AddressableEntity /// The hex-encoded address of the Package. /// [JsonPropertyName("package_hash")] - public string PackageHash { get; init; } + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public PackageKey Package { get; init; } /// /// The hash address of the contract wasm. From e53edd6fafed6e5fa6426eb9be7c17f3c4abf037 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Sun, 7 Jul 2024 12:09:31 +0200 Subject: [PATCH 049/126] bugfix parse correctly BlockHeader from QueryGlobalState for v156 network. Signed-off-by: David Hernando --- Casper.Network.SDK/Types/Block.cs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/Casper.Network.SDK/Types/Block.cs b/Casper.Network.SDK/Types/Block.cs index 3b3aeb9..b84e062 100644 --- a/Casper.Network.SDK/Types/Block.cs +++ b/Casper.Network.SDK/Types/Block.cs @@ -188,21 +188,24 @@ public override BlockHeader Read( { try { - reader.Read(); - var version = reader.GetString(); - reader.Read(); - switch (version) + using (JsonDocument document = JsonDocument.ParseValue(ref reader)) { - case "Version1": - var blockHeaderv1 = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - return (BlockHeader)blockHeaderv1; - case "Version2": - var blockHeaderv2 = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - return (BlockHeader)blockHeaderv2; - default: - throw new JsonException("Expected Version1 or Version2"); + if (document.RootElement.TryGetProperty("parent_hash", out var header)) + { + var blockHeaderV1 = JsonSerializer.Deserialize(document.RootElement.GetRawText()); + return (BlockHeader)blockHeaderV1; + } + if (document.RootElement.TryGetProperty("Version1", out var headerV1)) + { + var blockHeaderV1 = JsonSerializer.Deserialize(headerV1.GetRawText()); + return (BlockHeader)blockHeaderV1; + } + if (document.RootElement.TryGetProperty("Version2", out var headerV2)) + { + var blockHeaderV2 = JsonSerializer.Deserialize(headerV1.GetRawText()); + return (BlockHeader)blockHeaderV2; + } + throw new JsonException("Cannot deserialize BlockHeader"); } } catch (Exception e) From 0582902e28cd7c63112f573d5bfed6a791e4c811 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Sun, 7 Jul 2024 12:10:27 +0200 Subject: [PATCH 050/126] fixed EntryPoint parsing for GetStateItem on v156. Signed-off-by: David Hernando --- .../Casper.Network.SDK.Test.csproj | 4 ++ .../RPCResponses/GetEntityResultTest.cs | 15 +++-- .../JsonRpc/ResultTypes/GetEntityResult.cs | 6 +- Casper.Network.SDK/Types/EntryPoint.cs | 64 ++++++++++++++++--- 4 files changed, 70 insertions(+), 19 deletions(-) diff --git a/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj b/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj index 70b9c66..874ef3d 100644 --- a/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj +++ b/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj @@ -32,5 +32,9 @@ PreserveNewest + + + + diff --git a/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs index 56c0018..b11ed89 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs @@ -54,13 +54,14 @@ public void GetEntityContractTest_v200() Assert.AreEqual("uref-96cd0453fb2e1d063c9438c158c0d804d0121a96f3423046150ee355cfadefb6-007", result.NamedKeys[1].Key.ToString().ToLower()); Assert.IsTrue(result.EntryPoints.Count > 0); - Assert.AreEqual("allowance", result.EntryPoints[1].Name); - Assert.AreEqual(2, result.EntryPoints[1].Args.Count); - Assert.AreEqual("spender", result.EntryPoints[1].Args[1].Name); - Assert.AreEqual(CLType.Key, result.EntryPoints[1].Args[1].CLType); - Assert.AreEqual(CLType.U256, result.EntryPoints[1].Ret); - Assert.IsTrue(result.EntryPoints[1].Access.IsPublic); - Assert.AreEqual(EntryPointType.Called, result.EntryPoints[1].EntryPointType); + Assert.AreEqual("allowance", result.EntryPoints[1].V1CasperVm.Name); + Assert.AreEqual(2, result.EntryPoints[1].V1CasperVm.Args.Count); + Assert.AreEqual("spender", result.EntryPoints[1].V1CasperVm.Args[1].Name); + Assert.AreEqual(CLType.Key, result.EntryPoints[1].V1CasperVm.Args[1].CLType.Type); + Assert.AreEqual(CLType.U256, result.EntryPoints[1].V1CasperVm.Ret.Type); + Assert.IsTrue(result.EntryPoints[1].V1CasperVm.Access.IsPublic); + Assert.AreEqual(EntryPointType.Called, result.EntryPoints[1].V1CasperVm.EntryPointType); + Assert.AreEqual(EntryPointPayment.Caller, result.EntryPoints[1].V1CasperVm.EntryPointPayment); } } } \ No newline at end of file diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs index 813f656..14ed152 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs @@ -25,7 +25,7 @@ public class GetEntityResult : RpcResult /// /// Array of entry points defined in the entity /// - public List EntryPoints { get; init; } + public List EntryPoints { get; init; } /// /// A legacy account. @@ -47,7 +47,7 @@ public override GetEntityResult Read( string api_version = null; AddressableEntity entity = null; var namedKeys = new List(); - var entryPoints = new List(); + var entryPoints = new List(); Account legacyAccount = null; string merkle_proof = null; uint skippedEntityWrapperCount = 0; @@ -75,7 +75,7 @@ public override GetEntityResult Read( namedKeys = JsonSerializer.Deserialize>(ref reader, options); break; case "entry_points": - entryPoints = JsonSerializer.Deserialize>(ref reader, options); + entryPoints = JsonSerializer.Deserialize>(ref reader, options); break; case "LegacyAccount": if (reader.TokenType != JsonTokenType.Null) diff --git a/Casper.Network.SDK/Types/EntryPoint.cs b/Casper.Network.SDK/Types/EntryPoint.cs index f69a8f0..9283222 100644 --- a/Casper.Network.SDK/Types/EntryPoint.cs +++ b/Casper.Network.SDK/Types/EntryPoint.cs @@ -95,8 +95,32 @@ public enum EntryPointType /// Extract a subset of bytecode and installs it as a new smart contract. Runs using the called entity's context. /// Factory, + /// + /// Casper v1.x only. Executes in the contract context. + /// + Contract, + /// + /// Casper v1.x only. Executes in the caller account context. + /// + Session, } + public enum EntryPointPayment + { + /// + /// The caller must cover cost. + /// + Caller, + /// + /// Will cover cost to execute self but not cost of any subsequent invoked contracts. + /// + SelfOnly, + /// + /// Will cover cost to execute self and the cost of any subsequent invoked contracts. + /// + SelfOnward, + } + /// /// Parameter to a method /// @@ -141,6 +165,13 @@ public class EntryPoint [JsonConverter(typeof(JsonStringEnumConverter))] public EntryPointType EntryPointType { get; init; } + /// + /// Specifies who pays for the invocation and execution of the entrypoint. + /// + [JsonPropertyName("entry_point_payment")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public EntryPointPayment EntryPointPayment { get; init; } + /// /// Name of the entry point /// @@ -153,6 +184,30 @@ public class EntryPoint [JsonPropertyName("ret")] [JsonConverter(typeof(CLTypeInfoConverter))] public CLTypeInfo Ret { get; init; } + } + + public class EntryPointV2 + { + public EntryPointV2() + { + throw new NotImplementedException("V2CasperVm entry point not yet implemented"); + } + } + + public class VersionedEntryPoint + { + public EntryPoint V1CasperVm { get; init; } + + public EntryPointV2 V2CasperVm { get; init; } + } + + public class NamedEntryPoint + { + [JsonPropertyName("name")] + public string Name { get; init; } + + [JsonPropertyName("entry_point")] + public EntryPoint EntryPoint { get; init; } public class NamedEntryPointsConverter : JsonConverter> { @@ -177,13 +232,4 @@ public override void Write( } } } - - public class NamedEntryPoint - { - [JsonPropertyName("name")] - public string Name { get; init; } - - [JsonPropertyName("entry_point")] - public EntryPoint EntryPoint { get; init; } - } } \ No newline at end of file From 6d511c1d09979a45d07b2b08a6558560b9093240 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Sun, 7 Jul 2024 12:15:18 +0200 Subject: [PATCH 051/126] remove entry points from AddressableEntity.cs Signed-off-by: David Hernando --- Casper.Network.SDK/Types/AddressableEntity.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Casper.Network.SDK/Types/AddressableEntity.cs b/Casper.Network.SDK/Types/AddressableEntity.cs index 41f761b..6548507 100644 --- a/Casper.Network.SDK/Types/AddressableEntity.cs +++ b/Casper.Network.SDK/Types/AddressableEntity.cs @@ -170,13 +170,6 @@ public class AddressableEntity [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public URef MainPurse { get; init; } - /// - /// List of entry points or methods in the package. - /// - [JsonPropertyName("entry_points")] - [JsonConverter(typeof(EntryPoint.NamedEntryPointsConverter))] - public List EntryPoints { get; init; } - /// /// Set of public keys allowed to provide signatures on deploys for the package /// From f22b32d3e6fd49d4f92bb11769ca7acba4eb1486 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 8 Jul 2024 09:47:25 +0200 Subject: [PATCH 052/126] CSDK-120, CSDK-121, get/put transaction Signed-off-by: David Hernando --- Casper.Network.SDK.Test/CEP57ChecksumTest.cs | 53 -- .../Casper.Network.SDK.Test.csproj | 10 +- .../DeployItemByteSerializerTest.cs | 4 +- Casper.Network.SDK.Test/GlobalStateKeyTest.cs | 35 +- .../NctlMyDictContractTest.cs | 2 +- .../RPCResponses/GetAuctionInfoResultTest.cs | 64 ++ .../RPCResponses/GetDeployResultTest.cs | 79 +++ .../RPCResponses/GetEntityResultTest.cs | 67 ++ Casper.Network.SDK.Test/SSETypesTest.cs | 101 +++ .../TestData/block_added_v2.json | 44 ++ .../TestData/finality_signature_v156.json | 8 + .../TestData/finality_signature_v2.json | 12 + .../TestData/get-auction-info-v156.json | 87 +++ .../TestData/get-auction-info-v200.json | 401 +++++++++++ .../get-deploy-result-version1-failure.json | 245 +++++++ .../get-deploy-result-version1-success.json | 632 ++++++++++++++++++ .../get-deploy-result-version2-success.json | 225 +++++++ .../TestData/get-entity-account-v200.json | 31 + .../TestData/get-entity-contract-v200.json | 385 +++++++++++ .../sse-transaction-processed-v200.json | 202 ++++++ Casper.Network.SDK.Test/TransferDeployTest.cs | 2 +- .../ByteSerializers/BaseByteSerializer.cs | 12 + .../DeployApprovalByteSerializer.cs | 4 +- .../TransactionV1ByteSerializer.cs | 189 ++++++ Casper.Network.SDK/Casper.Network.SDK.csproj | 2 +- .../Converters/BidsListConverter.cs | 9 +- .../Converters/HexBytesConverter.cs | 22 - Casper.Network.SDK/ICasperClient.cs | 16 + Casper.Network.SDK/JsonRpc/CasperMethods.cs | 85 +++ .../JsonRpc/ResultTypes/GetDeployResult.cs | 95 ++- .../JsonRpc/ResultTypes/GetEntityResult.cs | 121 ++++ .../ResultTypes/GetTransactionResult.cs | 23 + .../ResultTypes/PutTransactionResult.cs | 17 + .../ResultTypes/SpeculativeExecutionResult.cs | 4 +- Casper.Network.SDK/NetCasperClient.cs | 124 +++- Casper.Network.SDK/SSE/DeployAccepted.cs | 2 +- Casper.Network.SDK/SSE/DeployProcessed.cs | 6 +- Casper.Network.SDK/SSE/FinalitySignature.cs | 203 +++++- Casper.Network.SDK/SSE/ISSEClient.cs | 4 + Casper.Network.SDK/SSE/SSEvent.cs | 9 + Casper.Network.SDK/SSE/ServerEventsClient.cs | 43 +- Casper.Network.SDK/SSE/Step.cs | 7 +- Casper.Network.SDK/SSE/TransactionAccepted.cs | 9 + Casper.Network.SDK/SSE/TransactionExpired.cs | 14 + .../SSE/TransactionProcessed.cs | 54 ++ Casper.Network.SDK/Types/Account.cs | 6 + Casper.Network.SDK/Types/AddressableEntity.cs | 179 +++++ .../Types/{DeployApproval.cs => Approval.cs} | 5 +- Casper.Network.SDK/Types/Bid.cs | 12 +- Casper.Network.SDK/Types/BidKind.cs | 97 +++ Casper.Network.SDK/Types/Block.cs | 31 +- Casper.Network.SDK/Types/ByteCode.cs | 17 + Casper.Network.SDK/Types/CLValue.cs | 2 +- Casper.Network.SDK/Types/ContractPackage.cs | 2 +- Casper.Network.SDK/Types/Delegator.cs | 143 ++-- Casper.Network.SDK/Types/Deploy.cs | 18 +- Casper.Network.SDK/Types/DeployHeader.cs | 1 - Casper.Network.SDK/Types/EntryPoint.cs | 98 ++- .../Types/ExecutableDeployItem.cs | 2 - Casper.Network.SDK/Types/ExecutionEffect.cs | 4 +- Casper.Network.SDK/Types/ExecutionInfo.cs | 26 + Casper.Network.SDK/Types/ExecutionResult.cs | 254 ++++++- .../GlobalStateKey/AddressableEntityKey.cs | 59 +- .../Types/GlobalStateKey/BalanceHoldKey.cs | 4 +- .../Types/GlobalStateKey/BidAddrKey.cs | 5 +- .../GlobalStateKey/BlockGlobalAddrKey.cs | 6 +- .../Types/GlobalStateKey/ByteCodeKey.cs | 6 +- .../Types/GlobalStateKey/EntryPointKey.cs | 47 ++ .../Types/GlobalStateKey/GlobalStateKey.cs | 99 ++- .../Types/GlobalStateKey/MessageKey.cs | 23 +- .../Types/GlobalStateKey/NamedKeyKey.cs | 12 +- .../Types/GlobalStateKey/PackageKey.cs | 4 +- Casper.Network.SDK/Types/IEntityIdentifier.cs | 9 + Casper.Network.SDK/Types/InitiatorAddr.cs | 2 + Casper.Network.SDK/Types/Message.cs | 81 +++ Casper.Network.SDK/Types/NamedKey.cs | 18 + Casper.Network.SDK/Types/Package.cs | 107 +++ Casper.Network.SDK/Types/PricingMode.cs | 161 +++++ Casper.Network.SDK/Types/PublicKey.cs | 13 +- Casper.Network.SDK/Types/Reservation.cs | 19 + Casper.Network.SDK/Types/Signature.cs | 9 +- Casper.Network.SDK/Types/StoredValue.cs | 50 +- Casper.Network.SDK/Types/Transaction.cs | 22 + .../Types/TransactionEntryPoint.cs | 116 ++++ Casper.Network.SDK/Types/TransactionHash.cs | 2 +- .../Types/TransactionScheduling.cs | 105 +++ Casper.Network.SDK/Types/TransactionTarget.cs | 331 +++++++++ Casper.Network.SDK/Types/TransactionV1.cs | 232 +++++++ Casper.Network.SDK/Types/TransactionV1Body.cs | 43 ++ .../Types/TransactionV1Header.cs | 53 ++ Casper.Network.SDK/Types/Transform.cs | 201 ++++-- Casper.Network.SDK/Types/TransformV1.cs | 199 ++++++ Casper.Network.SDK/Types/URef.cs | 6 +- Casper.Network.SDK/Types/UnbondingPurse.cs | 11 +- Casper.Network.SDK/Types/ValidatorBid.cs | 57 ++ Casper.Network.SDK/Types/ValidatorWeight.cs | 2 +- Casper.Network.SDK/Utils/CEP57Checksum.cs | 22 - 97 files changed, 6338 insertions(+), 458 deletions(-) create mode 100644 Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs create mode 100644 Casper.Network.SDK.Test/RPCResponses/GetDeployResultTest.cs create mode 100644 Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs create mode 100644 Casper.Network.SDK.Test/SSETypesTest.cs create mode 100644 Casper.Network.SDK.Test/TestData/block_added_v2.json create mode 100644 Casper.Network.SDK.Test/TestData/finality_signature_v156.json create mode 100644 Casper.Network.SDK.Test/TestData/finality_signature_v2.json create mode 100644 Casper.Network.SDK.Test/TestData/get-auction-info-v156.json create mode 100644 Casper.Network.SDK.Test/TestData/get-auction-info-v200.json create mode 100644 Casper.Network.SDK.Test/TestData/get-deploy-result-version1-failure.json create mode 100644 Casper.Network.SDK.Test/TestData/get-deploy-result-version1-success.json create mode 100644 Casper.Network.SDK.Test/TestData/get-deploy-result-version2-success.json create mode 100644 Casper.Network.SDK.Test/TestData/get-entity-account-v200.json create mode 100644 Casper.Network.SDK.Test/TestData/get-entity-contract-v200.json create mode 100644 Casper.Network.SDK.Test/TestData/sse-transaction-processed-v200.json create mode 100644 Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs create mode 100644 Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs create mode 100644 Casper.Network.SDK/JsonRpc/ResultTypes/GetTransactionResult.cs create mode 100644 Casper.Network.SDK/JsonRpc/ResultTypes/PutTransactionResult.cs create mode 100644 Casper.Network.SDK/SSE/TransactionAccepted.cs create mode 100644 Casper.Network.SDK/SSE/TransactionExpired.cs create mode 100644 Casper.Network.SDK/SSE/TransactionProcessed.cs create mode 100644 Casper.Network.SDK/Types/AddressableEntity.cs rename Casper.Network.SDK/Types/{DeployApproval.cs => Approval.cs} (85%) create mode 100644 Casper.Network.SDK/Types/BidKind.cs create mode 100644 Casper.Network.SDK/Types/ByteCode.cs create mode 100644 Casper.Network.SDK/Types/ExecutionInfo.cs create mode 100644 Casper.Network.SDK/Types/GlobalStateKey/EntryPointKey.cs create mode 100644 Casper.Network.SDK/Types/IEntityIdentifier.cs create mode 100644 Casper.Network.SDK/Types/Message.cs create mode 100644 Casper.Network.SDK/Types/Package.cs create mode 100644 Casper.Network.SDK/Types/PricingMode.cs create mode 100644 Casper.Network.SDK/Types/Reservation.cs create mode 100644 Casper.Network.SDK/Types/Transaction.cs create mode 100644 Casper.Network.SDK/Types/TransactionEntryPoint.cs create mode 100644 Casper.Network.SDK/Types/TransactionScheduling.cs create mode 100644 Casper.Network.SDK/Types/TransactionTarget.cs create mode 100644 Casper.Network.SDK/Types/TransactionV1.cs create mode 100644 Casper.Network.SDK/Types/TransactionV1Body.cs create mode 100644 Casper.Network.SDK/Types/TransactionV1Header.cs create mode 100644 Casper.Network.SDK/Types/TransformV1.cs create mode 100644 Casper.Network.SDK/Types/ValidatorBid.cs diff --git a/Casper.Network.SDK.Test/CEP57ChecksumTest.cs b/Casper.Network.SDK.Test/CEP57ChecksumTest.cs index 0b05f28..f657e44 100644 --- a/Casper.Network.SDK.Test/CEP57ChecksumTest.cs +++ b/Casper.Network.SDK.Test/CEP57ChecksumTest.cs @@ -24,59 +24,6 @@ public void CEP57LongByteArray() Assert.AreEqual(longHex.ToLower(), encodedHex); } - [Test] - public void CEP57DecodeEncode() - { - int result; - - var hash = "66B754c5E2981B41D41af39B88C8583bD08EF176eB1a81F56b8F395685805968"; - var bytes = CEP57Checksum.Decode(hash, out result); - Assert.AreEqual(CEP57Checksum.ValidChecksum, result); - - GlobalStateKey key = new HashKey(bytes); - Assert.AreEqual($"hash-{hash}", key.ToString()); - - hash = "eA1D6C19ccAeb35Ae717065c250E0F7F6Dc64AC3c6494a797E0b33A23CA1f1b9"; - bytes = CEP57Checksum.Decode(hash, out result); - Assert.AreEqual(CEP57Checksum.ValidChecksum, result); - Assert.AreEqual(hash, CEP57Checksum.Encode(bytes)); - - key = new TransferKey(bytes); - Assert.AreEqual($"transfer-{hash}", key.ToString()); - - hash = "98d945f5324F865243B7c02C0417AB6eaC361c5c56602FD42ced834a1Ba201B6"; - bytes = CEP57Checksum.Decode(hash, out result); - Assert.AreEqual(CEP57Checksum.ValidChecksum, result); - Assert.AreEqual(hash, CEP57Checksum.Encode(bytes)); - - key = new DeployInfoKey(bytes); - Assert.AreEqual($"deploy-{hash}", key.ToString()); - - hash = "8cf5E4aCF51f54Eb59291599187838Dc3BC234089c46fc6cA8AD17e762aE4401"; - bytes = CEP57Checksum.Decode(hash, out result); - Assert.AreEqual(CEP57Checksum.ValidChecksum, result); - Assert.AreEqual(hash, CEP57Checksum.Encode(bytes)); - - key = new BalanceKey(bytes); - Assert.AreEqual($"balance-{hash}", key.ToString()); - - hash = "010c3Fe81B7b862E50C77EF9A958a05BfA98444F26f96f23d37A13c96244cFB7"; - bytes = CEP57Checksum.Decode(hash, out result); - Assert.AreEqual(CEP57Checksum.ValidChecksum, result); - Assert.AreEqual(hash, CEP57Checksum.Encode(bytes)); - - key = new BidKey(bytes); - Assert.AreEqual($"bid-{hash}", key.ToString()); - - hash = "98d945f5324F865243B7c02C0417AB6eaC361c5c56602FD42ced834a1Ba201B6"; - bytes = CEP57Checksum.Decode(hash, out result); - Assert.AreEqual(CEP57Checksum.ValidChecksum, result); - Assert.AreEqual(hash, CEP57Checksum.Encode(bytes)); - - key = new WithdrawKey(bytes); - Assert.AreEqual($"withdraw-{hash}", key.ToString()); - } - [Test] public void CEP57HasChecksum() { diff --git a/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj b/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj index 70b9c66..2e4c5b7 100644 --- a/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj +++ b/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj @@ -32,5 +32,13 @@ PreserveNewest - + + + + + + + + + diff --git a/Casper.Network.SDK.Test/DeployItemByteSerializerTest.cs b/Casper.Network.SDK.Test/DeployItemByteSerializerTest.cs index 69a2b67..4658738 100644 --- a/Casper.Network.SDK.Test/DeployItemByteSerializerTest.cs +++ b/Casper.Network.SDK.Test/DeployItemByteSerializerTest.cs @@ -40,7 +40,7 @@ public void StoredContractByHashSerialization() new StoredContractByHashDeployItem(hash.ToHexString(), "counter_inc"); - Assert.AreEqual(CEP57Checksum.Encode(hash.RawBytes), storedContract.Hash); + Assert.AreEqual(Hex.ToHexString(hash.RawBytes), storedContract.Hash); Assert.AreEqual("counter_inc", storedContract.EntryPoint); Assert.IsNotNull(storedContract.RuntimeArgs); Assert.AreEqual(0, storedContract.RuntimeArgs.Count); @@ -71,7 +71,7 @@ public void StoredVersionedContractByHashSerialization() var storedContract = new StoredVersionedContractByHashDeployItem(hash.ToHexString(), 1, "counter_inc"); - Assert.AreEqual(CEP57Checksum.Encode(hash.RawBytes), storedContract.Hash); + Assert.AreEqual(Hex.ToHexString(hash.RawBytes), storedContract.Hash); Assert.AreEqual(1, storedContract.Version); Assert.AreEqual("counter_inc", storedContract.EntryPoint); Assert.IsNotNull(storedContract.RuntimeArgs); diff --git a/Casper.Network.SDK.Test/GlobalStateKeyTest.cs b/Casper.Network.SDK.Test/GlobalStateKeyTest.cs index f557889..32aeda5 100644 --- a/Casper.Network.SDK.Test/GlobalStateKeyTest.cs +++ b/Casper.Network.SDK.Test/GlobalStateKeyTest.cs @@ -409,7 +409,7 @@ public void InvalidPrefixTest() [Test] public void InvalidKeyIdentifierTest() { - var bytes = Hex.Decode("0F00"); + var bytes = Hex.Decode("3F00"); var ex = Assert.Catch(() => { GlobalStateKey.FromBytes(bytes); @@ -417,35 +417,6 @@ public void InvalidKeyIdentifierTest() Assert.IsNotNull(ex); Assert.IsTrue(ex.Message.StartsWith("Unknown key identifier")); } - - [Test] - public void CEP57ChecksumValidationTest() - { - var hashKey = "hash-98d945f5324F865243B7c02C0417AB6eaC361c5c56602FD42ced834a1Ba201B6"; - - var key = GlobalStateKey.FromString(hashKey); - Assert.IsNotNull(key); - - var invalidHashKey = "hash-98D945F5324F865243B7c02C0417AB6eaC361c5c56602FD42ced834a1Ba201B6"; - var ex = Assert.Catch(() => - { - GlobalStateKey.FromString(invalidHashKey); - }); - Assert.IsNotNull(ex); - Assert.IsTrue(ex.Message.StartsWith("Global State Key checksum mismatch.")); - - var urefKey = "uref-98d945f5324F865243B7c02C0417AB6eaC361c5c56602FD42ced834a1Ba201B6-007"; - key = GlobalStateKey.FromString(urefKey); - Assert.IsNotNull(key); - - var invalidURefKey = "uref-98D945F5324F865243B7c02C0417AB6eaC361c5c56602FD42ced834a1Ba201B6-007"; - ex = Assert.Catch(() => - { - GlobalStateKey.FromString(invalidURefKey); - }); - Assert.IsNotNull(ex); - Assert.IsTrue(ex.Message.StartsWith("URef checksum mismatch.")); - } [Test] public void HashKeyJsonDeserializeTest() @@ -467,7 +438,7 @@ public void AddressableEntityKeyTest() var entityAddr = "0101010101010101010101010101010101010101010101010101010101010101"; - var entities = new EntityKind[] { EntityKind.System, EntityKind.Account, EntityKind.Contract }; + var entities = new EntityKindEnum[] { EntityKindEnum.System, EntityKindEnum.Account, EntityKindEnum.Contract }; foreach (var entityKind in entities) { var entityKey = $"{ENTITY_PREFIX}{entityKind.ToString().ToLower()}-{entityAddr}"; @@ -513,7 +484,7 @@ public void NamedKeyKeyTest() var entityAddr = "0101010101010101010101010101010101010101010101010101010101010101"; var namedKeyAddr = "0202020202020202020202020202020202020202020202020202020202020202"; - var entities = new EntityKind[] { EntityKind.System, EntityKind.Account, EntityKind.Contract }; + var entities = new EntityKindEnum[] { EntityKindEnum.System, EntityKindEnum.Account, EntityKindEnum.Contract }; foreach (var entityKind in entities) { var namedKeyKey = $"{NAMEDKEY_PREFIX}{ENTITY_PREFIX}{entityKind.ToString().ToLower()}-{entityAddr}-{namedKeyAddr}"; diff --git a/Casper.Network.SDK.Test/NctlMyDictContractTest.cs b/Casper.Network.SDK.Test/NctlMyDictContractTest.cs index 5e3a2e2..b11f659 100644 --- a/Casper.Network.SDK.Test/NctlMyDictContractTest.cs +++ b/Casper.Network.SDK.Test/NctlMyDictContractTest.cs @@ -49,7 +49,7 @@ public async Task WaitContractDeploymentTest() var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); var getResponse = await _client.GetDeploy(_contractDeployHash, tokenSource.Token); - var execResult = getResponse.Parse().ExecutionResults.First(); + var execResult = getResponse.Parse().ExecutionResult; Assert.IsTrue(execResult.IsSuccess); Assert.AreEqual(64, execResult.BlockHash.Length); Assert.IsNull(execResult.ErrorMessage); diff --git a/Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs new file mode 100644 index 0000000..df8a415 --- /dev/null +++ b/Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs @@ -0,0 +1,64 @@ +using System.IO; +using System.Linq; +using System.Numerics; +using Casper.Network.SDK.JsonRpc.ResultTypes; +using Casper.Network.SDK.Types; +using NUnit.Framework; +using Org.BouncyCastle.Utilities.Encoders; + +namespace NetCasperTest.RPCResponses +{ + public class GetAuctionInfoResultTest + { + [Test] + public void GetBlockResultTest_v156() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-auction-info-v156.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("1.5.6", result.ApiVersion); + Assert.AreEqual("436d8e272b759838e6416d915efb3adc3a102489b8db1c0ce731043116c6f645", result.AuctionState.StateRootHash); + Assert.AreEqual(3192405, result.AuctionState.BlockHeight); + Assert.AreEqual(1, result.AuctionState.EraValidators.Count); + Assert.AreEqual(13801, result.AuctionState.EraValidators[0].EraId); + Assert.AreEqual(5, result.AuctionState.EraValidators[0].ValidatorWeights.Count); + Assert.AreEqual("020377bc3ad54b5505971e001044ea822a3f6f307f8dc93fa45a05b7463c0a053bed", result.AuctionState.EraValidators[0].ValidatorWeights[4].PublicKey.ToString().ToLower()); + Assert.AreEqual(BigInteger.Parse("10567495110201092"), result.AuctionState.EraValidators[0].ValidatorWeights[4].Weight); + + Assert.AreEqual(3, result.AuctionState.Bids.Count); + Assert.AreEqual("01001b79b9a6e13d2b96e916f7fa7dff40496ba5188479263ca0fb2ccf8b714305", result.AuctionState.Bids[0].PublicKey.ToString().ToLower()); + Assert.AreEqual(1, result.AuctionState.Bids[0].Delegators.Count); + Assert.AreEqual("018b34b15e023844531621cb52d42e216a2ea56034f0f40bf7cee566c32eae4f83", result.AuctionState.Bids[0].Delegators[0].DelegatorPublicKey.ToString().ToLower()); + Assert.AreEqual(BigInteger.Parse("30268476029"), result.AuctionState.Bids[0].Delegators[0].StakedAmount); + Assert.IsNull(result.AuctionState.Bids[0].Delegators[0].VestingSchedule); + } + + [Test] + public void GetBlockResultTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-auction-info-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + Assert.AreEqual("1e8f22fb799932c56ffcf4d48c014e09ba9b791a3280f9a8cc9c7614ce7d562e", result.AuctionState.StateRootHash); + Assert.AreEqual(1394, result.AuctionState.BlockHeight); + Assert.AreEqual(1394, result.AuctionState.BlockHeight); + Assert.AreEqual(3, result.AuctionState.EraValidators.Count); + Assert.AreEqual(128, result.AuctionState.EraValidators[2].EraId); + Assert.AreEqual(5, result.AuctionState.EraValidators[2].ValidatorWeights.Count); + Assert.AreEqual("01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", result.AuctionState.EraValidators[2].ValidatorWeights[4].PublicKey.ToString().ToLower()); + Assert.AreEqual(BigInteger.Parse("1366181433007372460"), result.AuctionState.EraValidators[2].ValidatorWeights[4].Weight); + + Assert.AreEqual(5, result.AuctionState.Bids.Count); + Assert.AreEqual("01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", result.AuctionState.Bids[4].PublicKey.ToString().ToLower()); + Assert.AreEqual(3, result.AuctionState.Bids[4].Delegators.Count); + Assert.AreEqual("0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55", result.AuctionState.Bids[4].Delegators[1].DelegatorPublicKey.ToString().ToLower()); + Assert.AreEqual(BigInteger.Parse("654063155243124749"), result.AuctionState.Bids[4].Delegators[1].StakedAmount); + Assert.AreEqual(1719301233872, result.AuctionState.Bids[4].Delegators[1].VestingSchedule.InitialReleaseTimestampMillis); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/RPCResponses/GetDeployResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetDeployResultTest.cs new file mode 100644 index 0000000..be2ae0d --- /dev/null +++ b/Casper.Network.SDK.Test/RPCResponses/GetDeployResultTest.cs @@ -0,0 +1,79 @@ +using System.IO; +using System.Linq; +using Casper.Network.SDK.JsonRpc.ResultTypes; +using Casper.Network.SDK.Types; +using NUnit.Framework; +using Org.BouncyCastle.Utilities.Encoders; + +namespace NetCasperTest.RPCResponses +{ + public class GetDeployResultTest + { + [Test] + public void GetDeployResultTest_Version1_Success() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-deploy-result-version1-success.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + + var deploy = result.Deploy; + AssertExtensions.IsHash(deploy.Hash); + AssertExtensions.IsValidHex(deploy.Header.Account.ToAccountHex(), 33); + Assert.AreEqual(1, deploy.Payment.RuntimeArgs.Count); + Assert.AreEqual(6, deploy.Session.RuntimeArgs.Count); + Assert.AreEqual(1, deploy.Approvals.Count); + AssertExtensions.IsHash(result.ExecutionInfo.BlockHash); + Assert.AreEqual(23, result.ExecutionInfo.BlockHeight); + Assert.IsTrue(result.ExecutionInfo.ExecutionResult.Effect.Count > 0); + Assert.IsNull(result.ExecutionInfo.ExecutionResult.ErrorMessage); + } + + [Test] + public void GetDeployResultTest_Version1_Failure() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-deploy-result-version1-failure.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + + var deploy = result.Deploy; + AssertExtensions.IsHash(deploy.Hash); + AssertExtensions.IsValidHex(deploy.Header.Account.ToAccountHex(), 33); + Assert.AreEqual(1, deploy.Payment.RuntimeArgs.Count); + Assert.AreEqual(2, deploy.Session.RuntimeArgs.Count); + Assert.AreEqual(1, deploy.Approvals.Count); + AssertExtensions.IsHash(result.ExecutionInfo.BlockHash); + Assert.AreEqual(122, result.ExecutionInfo.BlockHeight); + Assert.IsTrue(result.ExecutionInfo.ExecutionResult.Effect.Count > 0); + Assert.AreEqual("User error: 60001", result.ExecutionInfo.ExecutionResult.ErrorMessage); + Assert.AreEqual("31057410", result.ExecutionInfo.ExecutionResult.Cost.ToString()); + } + + [Test] + public void GetDeployResultTest_Version2_Success() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-deploy-result-version2-success.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + + var deploy = result.Deploy; + AssertExtensions.IsHash(deploy.Hash); + AssertExtensions.IsValidHex(deploy.Header.Account.ToAccountHex(), 33); + Assert.AreEqual(1, deploy.Payment.RuntimeArgs.Count); + Assert.AreEqual(2, deploy.Session.RuntimeArgs.Count); + Assert.AreEqual(1, deploy.Approvals.Count); + AssertExtensions.IsHash(result.ExecutionInfo.BlockHash); + Assert.AreEqual(1964, result.ExecutionInfo.BlockHeight); + Assert.IsTrue(result.ExecutionInfo.ExecutionResult.Effect.Count > 0); + Assert.IsNull(result.ExecutionInfo.ExecutionResult.ErrorMessage); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs new file mode 100644 index 0000000..b11ed89 --- /dev/null +++ b/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs @@ -0,0 +1,67 @@ +using System.IO; +using Casper.Network.SDK.JsonRpc.ResultTypes; +using Casper.Network.SDK.Types; +using NUnit.Framework; + +namespace NetCasperTest.RPCResponses +{ + public class GetEntityResultTest + { + [Test] + public void GetEntityAccountTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-entity-account-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + + AssertExtensions.IsValidHex(result.MerkleProof); + + Assert.IsNotNull(result.Entity.Package); + Assert.IsNotNull(result.Entity.MainPurse); + Assert.AreEqual("byte-code-0000000000000000000000000000000000000000000000000000000000000000", result.Entity.ByteCodeHash); + Assert.IsNotEmpty(result.Entity.EntityKind.Account.ToHexString()); + Assert.AreEqual(1, result.Entity.ActionThresholds.KeyManagement); + Assert.AreEqual(1, result.Entity.ActionThresholds.Deployment); + Assert.AreEqual(1, result.Entity.ActionThresholds.UpgradeManagement); + Assert.AreEqual(0, result.NamedKeys.Count); + Assert.AreEqual(0, result.EntryPoints.Count); + } + + [Test] + public void GetEntityContractTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-entity-contract-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + + AssertExtensions.IsValidHex(result.MerkleProof); + + Assert.IsNotNull(result.Entity.Package); + Assert.IsNotNull(result.Entity.MainPurse); + Assert.AreEqual("byte-code-85def61e3ee02e10a1e845cfb8e8b2d9640a18f605333158027a24ed8569d895", result.Entity.ByteCodeHash); + Assert.AreEqual(TransactionRuntime.VmCasperV1, result.Entity.EntityKind.SmartContract); + Assert.AreEqual(1, result.Entity.ActionThresholds.KeyManagement); + Assert.AreEqual(1, result.Entity.ActionThresholds.Deployment); + Assert.AreEqual(1, result.Entity.ActionThresholds.UpgradeManagement); + Assert.AreEqual(11, result.NamedKeys.Count); + Assert.AreEqual("balances", result.NamedKeys[1].Name); + Assert.AreEqual("uref-96cd0453fb2e1d063c9438c158c0d804d0121a96f3423046150ee355cfadefb6-007", result.NamedKeys[1].Key.ToString().ToLower()); + + Assert.IsTrue(result.EntryPoints.Count > 0); + Assert.AreEqual("allowance", result.EntryPoints[1].V1CasperVm.Name); + Assert.AreEqual(2, result.EntryPoints[1].V1CasperVm.Args.Count); + Assert.AreEqual("spender", result.EntryPoints[1].V1CasperVm.Args[1].Name); + Assert.AreEqual(CLType.Key, result.EntryPoints[1].V1CasperVm.Args[1].CLType.Type); + Assert.AreEqual(CLType.U256, result.EntryPoints[1].V1CasperVm.Ret.Type); + Assert.IsTrue(result.EntryPoints[1].V1CasperVm.Access.IsPublic); + Assert.AreEqual(EntryPointType.Called, result.EntryPoints[1].V1CasperVm.EntryPointType); + Assert.AreEqual(EntryPointPayment.Caller, result.EntryPoints[1].V1CasperVm.EntryPointPayment); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/SSETypesTest.cs b/Casper.Network.SDK.Test/SSETypesTest.cs new file mode 100644 index 0000000..79b410f --- /dev/null +++ b/Casper.Network.SDK.Test/SSETypesTest.cs @@ -0,0 +1,101 @@ +using System.IO; +using System.Text.Json; +using Casper.Network.SDK.SSE; +using NUnit.Framework; + +namespace NetCasperTest +{ + public class SSETypesTest + { + [Test] + public void FinalitySignatureV1Test() + { + string testFile = TestContext.CurrentContext.TestDirectory + "/TestData/finality_signature_v156.json"; + var json = File.ReadAllText(testFile); + + var doc = System.Text.Json.JsonDocument.Parse(json); + + json = doc.RootElement.GetProperty("FinalitySignature").GetRawText(); + // Assert.AreEqual(3, doc.RootElement.EnumerateObject().Count()); + // Assert.AreEqual("U512", ); + var value = JsonSerializer.Deserialize(json); + Assert.IsNotNull(value); + + Assert.AreEqual(1, value.Version); + Assert.AreEqual("d800de72aa40c6df064c714d3a6d8b6fab73f68742d8468b84efd6616bbb10bb", value.BlockHash); + Assert.AreEqual(0, value.BlockHeight); + Assert.AreEqual(13859, value.EraId); + Assert.IsNull(value.ChainNameHash); + Assert.AreEqual( + "01221b61b83b889898363501c7defd7baa6729989d8fce2dfffea4632017fd46cc34b88bc85fa570a6e9ada829c67b9fdaa78e8dee2f07d346bcad0010e1d3df0f", + value.Signature.ToHexString().ToLowerInvariant()); + Assert.AreEqual("01b71b2d746681b4e0f44ef137d72ee0d42122b08ef569dc65bb0395cb624f99e5", + value.PublicKey.ToString().ToLowerInvariant()); + + var v1 = (FinalitySignatureV1)value; + + Assert.AreEqual("d800de72aa40c6df064c714d3a6d8b6fab73f68742d8468b84efd6616bbb10bb", v1.BlockHash); + Assert.AreEqual(13859, v1.EraId); + Assert.AreEqual( + "01221b61b83b889898363501c7defd7baa6729989d8fce2dfffea4632017fd46cc34b88bc85fa570a6e9ada829c67b9fdaa78e8dee2f07d346bcad0010e1d3df0f", + v1.Signature.ToHexString().ToLowerInvariant()); + Assert.AreEqual("01b71b2d746681b4e0f44ef137d72ee0d42122b08ef569dc65bb0395cb624f99e5", + v1.PublicKey.ToString().ToLowerInvariant()); + } + + [Test] + public void FinalitySignatureV2Test() + { + string testFile = TestContext.CurrentContext.TestDirectory + "/TestData/finality_signature_v2.json"; + var json = File.ReadAllText(testFile); + + var doc = System.Text.Json.JsonDocument.Parse(json); + + json = doc.RootElement.GetProperty("FinalitySignature").GetRawText(); + // Assert.AreEqual(3, doc.RootElement.EnumerateObject().Count()); + // Assert.AreEqual("U512", ); + var value = JsonSerializer.Deserialize(json); + Assert.IsNotNull(value); + + Assert.AreEqual(2, value.Version); + Assert.AreEqual("13a5603c1dad7d4e4d2ce81313c35043172e1535363c3ec428f559dff5704ea5", value.BlockHash); + Assert.AreEqual(54, value.BlockHeight); + Assert.AreEqual(5, value.EraId); + Assert.AreEqual("8a09603fc862b15412b60a050d71f69c57601b6da7382dd56b9a3f300822bb75", value.ChainNameHash); + Assert.AreEqual( + "01a12e3601b4d5c82177231910adaabf50d8a52416e756efee1694ee7534ae16fdb59a7370d564715615736074850b9255ee246c8ffa2502c167c6ef8a86b06504", + value.Signature.ToHexString().ToLowerInvariant()); + Assert.AreEqual("01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + value.PublicKey.ToString().ToLowerInvariant()); + } + + [Test] + public void TransactionProcessedTest() + { + string testFile = TestContext.CurrentContext.TestDirectory + + "/TestData/sse-transaction-processed-v200.json"; + var json = File.ReadAllText(testFile); + + var value = JsonSerializer.Deserialize(json); + Assert.IsNotNull(value); + + Assert.AreEqual("9749ce1dc5dbd0d9a611088f934fd81c2c8429dbab0a3a7d281359be0a92d29a", + value.TransactionHash.Version1); + Assert.AreEqual("01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", + value.InitiatorAddr.PublicKey.ToString().ToLower()); + Assert.AreEqual("b3fc60731e800dedfc60fcb1d85b91a93cf688237e53d2f8920e4fad4ab047ee", value.BlockHash); + Assert.IsTrue(value.ExecutionResult.Effect.Count > 0); + + Assert.AreEqual(1, value.Messages.Count); + var message = value.Messages[0]; + Assert.AreEqual("entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed", + message.AddressableEntity.ToString()); + Assert.IsNotEmpty(message.MessagePayload.String); + Assert.IsNull(message.MessagePayload.Bytes); + Assert.AreEqual("events", message.TopicName); + Assert.AreEqual("5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", message.TopicNameHash); + Assert.AreEqual(1, message.TopicIndex); + Assert.AreEqual(2, message.BlockIndex); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/block_added_v2.json b/Casper.Network.SDK.Test/TestData/block_added_v2.json new file mode 100644 index 0000000..9a33854 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/block_added_v2.json @@ -0,0 +1,44 @@ +{ + "BlockAdded": { + "block_hash": "13a5603c1dad7d4e4d2ce81313c35043172e1535363c3ec428f559dff5704ea5", + "block": { + "Version2": { + "hash": "13a5603c1dad7d4e4d2ce81313c35043172e1535363c3ec428f559dff5704ea5", + "header": { + "parent_hash": "8d14ccfde187f71c0d0b6a2e51298e851521a52e6df21ff8c572412477498106", + "state_root_hash": "a8a33e0b5a36950eda1ef918cb959a6bb0a56aeca356b6d670003d041bb348e9", + "body_hash": "337a4c9e510e01e142a19e5d81203bdc43e59a4f9039288c01f7b89370e1d104", + "random_bit": false, + "accumulated_seed": "b86abf8486be8b34621ac2d519886e894e1bef647b556e42a2d4c8b3104270a1", + "era_end": null, + "timestamp": "2024-05-22T07:22:36.684Z", + "era_id": 5, + "height": 54, + "protocol_version": "2.0.0", + "proposer": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "current_gas_price": 1, + "last_switch_block_hash": "1d0103963e72d14e760106267d28f4f20ef2eb08dc8a0101e5f18e6f18859af0" + }, + "body": { + "transactions": { + "0": [], + "1": [], + "2": [], + "3": [] + }, + "rewarded_signatures": [ + [ + 248 + ], + [ + 0 + ], + [ + 0 + ] + ] + } + } + } + } +} diff --git a/Casper.Network.SDK.Test/TestData/finality_signature_v156.json b/Casper.Network.SDK.Test/TestData/finality_signature_v156.json new file mode 100644 index 0000000..25a0d75 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/finality_signature_v156.json @@ -0,0 +1,8 @@ +{ + "FinalitySignature": { + "block_hash": "d800de72aa40c6df064c714d3a6d8b6fab73f68742d8468b84efd6616bbb10bb", + "era_id": 13859, + "signature": "01221b61b83b889898363501c7defd7baa6729989d8fce2dfffea4632017fd46cc34b88bc85fa570a6e9ada829c67b9fdaa78e8dee2f07d346bcad0010e1d3df0f", + "public_key": "01b71b2d746681b4e0f44ef137d72ee0d42122b08ef569dc65bb0395cb624f99e5" + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/finality_signature_v2.json b/Casper.Network.SDK.Test/TestData/finality_signature_v2.json new file mode 100644 index 0000000..6d94fee --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/finality_signature_v2.json @@ -0,0 +1,12 @@ +{ + "FinalitySignature": { + "V2": { + "block_hash": "13a5603c1dad7d4e4d2ce81313c35043172e1535363c3ec428f559dff5704ea5", + "block_height": 54, + "era_id": 5, + "chain_name_hash": "8a09603fc862b15412b60a050d71f69c57601b6da7382dd56b9a3f300822bb75", + "signature": "01a12e3601b4d5c82177231910adaabf50d8a52416e756efee1694ee7534ae16fdb59a7370d564715615736074850b9255ee246c8ffa2502c167c6ef8a86b06504", + "public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b" + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/get-auction-info-v156.json b/Casper.Network.SDK.Test/TestData/get-auction-info-v156.json new file mode 100644 index 0000000..b47f441 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-auction-info-v156.json @@ -0,0 +1,87 @@ +{ + "api_version": "1.5.6", + "auction_state": { + "state_root_hash": "436d8e272b759838e6416d915efb3adc3a102489b8db1c0ce731043116c6f645", + "block_height": 3192405, + "era_validators": [ + { + "era_id": 13801, + "validator_weights": [ + { + "public_key": "01ee3d205fe314de0739fac7264c97af5ccc4cffec391cae5bcb6f87e2dc2df079", + "weight": "1324008752218119" + }, + { + "public_key": "01f0c453c8f47ce5348227b59da3555f7e487477aab5f254f62216ad85df475c74", + "weight": "24633294030105" + }, + { + "public_key": "01f340df2c32f25391e8f7924a99e93cab3a6f230ff7af1cacbfc070772cbebd94", + "weight": "11456292357472638" + }, + { + "public_key": "01fe19d0fd010dfecc4f2a812c396bef932cd8f11b3855cc5ec3f6bd9545d58894", + "weight": "24228828948987" + }, + { + "public_key": "020377bc3ad54b5505971e001044ea822a3f6f307f8dc93fa45a05b7463c0a053bed", + "weight": "10567495110201092" + } + ] + } + ], + "bids": [ + { + "public_key": "01001b79b9a6e13d2b96e916f7fa7dff40496ba5188479263ca0fb2ccf8b714305", + "bid": { + "bonding_purse": "uref-68f12244cf9e37759aa78e3146c431cc4577fc122272989b9f9ebf2e8f27d741-007", + "staked_amount": "908982507030", + "delegation_rate": 10, + "delegators": [ + { + "public_key": "018b34b15e023844531621cb52d42e216a2ea56034f0f40bf7cee566c32eae4f83", + "staked_amount": "30268476029", + "bonding_purse": "uref-401f87167d733d8dd7d3efbf135a91ccd42fffb77b02d4a0075b963f14f1fbb4-007", + "delegatee": "01001b79b9a6e13d2b96e916f7fa7dff40496ba5188479263ca0fb2ccf8b714305" + } + ], + "inactive": true + } + }, + { + "public_key": "01001b79b9a6e13d2b96e916f7fa7dff40496ba5188479263ca0fb2ccf8b714305", + "bid": { + "bonding_purse": "uref-68f12244cf9e37759aa78e3146c431cc4577fc122272989b9f9ebf2e8f27d741-007", + "staked_amount": "908982507030", + "delegation_rate": 10, + "delegators": [ + { + "public_key": "018b34b15e023844531621cb52d42e216a2ea56034f0f40bf7cee566c32eae4f83", + "staked_amount": "30268476029", + "bonding_purse": "uref-401f87167d733d8dd7d3efbf135a91ccd42fffb77b02d4a0075b963f14f1fbb4-007", + "delegatee": "01001b79b9a6e13d2b96e916f7fa7dff40496ba5188479263ca0fb2ccf8b714305" + } + ], + "inactive": true + } + }, + { + "public_key": "01001b79b9a6e13d2b96e916f7fa7dff40496ba5188479263ca0fb2ccf8b714305", + "bid": { + "bonding_purse": "uref-68f12244cf9e37759aa78e3146c431cc4577fc122272989b9f9ebf2e8f27d741-007", + "staked_amount": "908982507030", + "delegation_rate": 10, + "delegators": [ + { + "public_key": "018b34b15e023844531621cb52d42e216a2ea56034f0f40bf7cee566c32eae4f83", + "staked_amount": "30268476029", + "bonding_purse": "uref-401f87167d733d8dd7d3efbf135a91ccd42fffb77b02d4a0075b963f14f1fbb4-007", + "delegatee": "01001b79b9a6e13d2b96e916f7fa7dff40496ba5188479263ca0fb2ccf8b714305" + } + ], + "inactive": true + } + } + ] + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/get-auction-info-v200.json b/Casper.Network.SDK.Test/TestData/get-auction-info-v200.json new file mode 100644 index 0000000..5c3ce33 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-auction-info-v200.json @@ -0,0 +1,401 @@ +{ + "api_version": "2.0.0", + "auction_state": { + "state_root_hash": "1e8f22fb799932c56ffcf4d48c014e09ba9b791a3280f9a8cc9c7614ce7d562e", + "block_height": 1394, + "era_validators": [ + { + "era_id": 126, + "validator_weights": [ + { + "public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "weight": "773530002936061175" + }, + { + "public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "weight": "2576240053903858104" + }, + { + "public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "weight": "1391364929868444236" + }, + { + "public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "weight": "743423726792740916" + }, + { + "public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "weight": "1345521286498896079" + } + ] + }, + { + "era_id": 127, + "validator_weights": [ + { + "public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "weight": "776022459374023984" + }, + { + "public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "weight": "2596547825066264181" + }, + { + "public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "weight": "1401840263593846706" + }, + { + "public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "weight": "751821266092616465" + }, + { + "public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "weight": "1358848185873249187" + } + ] + }, + { + "era_id": 128, + "validator_weights": [ + { + "public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "weight": "778514256836908775" + }, + { + "public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "weight": "2622847032216478396" + }, + { + "public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "weight": "1412321239623424126" + }, + { + "public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "weight": "760216038315816781" + }, + { + "public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "weight": "1366181433007372460" + } + ] + } + ], + "bids": [ + { + "public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "bid": { + "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "bonding_purse": "uref-7141e0057da2902a50a7a0794f8941321e3f32c477f67621239e7964151e8734-007", + "staked_amount": "406303529637252506", + "delegation_rate": 1, + "vesting_schedule": { + "initial_release_timestamp_millis": 1719301233872, + "locked_amounts": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0" + ] + }, + "delegators": [ + { + "delegator_public_key": "018b46617b2b97e633b36530f2964b3f4c15916235910a2737e83d4fa2c7fad542", + "delegator": { + "delegator_public_key": "018b46617b2b97e633b36530f2964b3f4c15916235910a2737e83d4fa2c7fad542", + "staked_amount": "372210727199656269", + "bonding_purse": "uref-841b81b467c6bc114096446d0e121273645f21d91e1e33c0de3fc9398b294aac-007", + "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "vesting_schedule": { + "initial_release_timestamp_millis": 1719301233872, + "locked_amounts": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0" + ] + } + } + } + ], + "inactive": false + } + }, + { + "public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "bid": { + "validator_public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "bonding_purse": "uref-d551374a39e56fb24bd5a3b3bf87a1ef7d4c08c6ce7892dcbb4a158ead9822c4-007", + "staked_amount": "1370640008489104648", + "delegation_rate": 1, + "vesting_schedule": { + "initial_release_timestamp_millis": 1719301233872, + "locked_amounts": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0" + ] + }, + "delegators": [ + { + "delegator_public_key": "0197b79d1a1351f8fb922b9f7f556d2bbfdba5105df9eaa6caa07804c703a641ed", + "delegator": { + "delegator_public_key": "0197b79d1a1351f8fb922b9f7f556d2bbfdba5105df9eaa6caa07804c703a641ed", + "staked_amount": "1252207023727373748", + "bonding_purse": "uref-cf1c24cc7b54a82ef379dcc87a3958ef0ffadf817a605b1e425d3a659946350b-007", + "validator_public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "vesting_schedule": { + "initial_release_timestamp_millis": 1719301233872, + "locked_amounts": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0" + ] + } + } + } + ], + "inactive": false + } + }, + { + "public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "bid": { + "validator_public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "bonding_purse": "uref-5af966267c401835bc3535c8517353eeed45c4e33cac29b50a4bb21d5a137305-007", + "staked_amount": "737000667377542259", + "delegation_rate": 1, + "vesting_schedule": { + "initial_release_timestamp_millis": 1719301233872, + "locked_amounts": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0" + ] + }, + "delegators": [ + { + "delegator_public_key": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", + "delegator": { + "delegator_public_key": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", + "staked_amount": "675320572245881867", + "bonding_purse": "uref-b22d651969ee70cd17d188f84dbdb6e12e0d8781c6f6615887ddd2ca4164dd7f-007", + "validator_public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "vesting_schedule": { + "initial_release_timestamp_millis": 1719301233872, + "locked_amounts": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0" + ] + } + } + } + ], + "inactive": false + } + }, + { + "public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "bid": { + "validator_public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "bonding_purse": "uref-ae3f94140d484ffdb28cf447e05edc64df13ca29adc33acca948fa73226b951b-007", + "staked_amount": "395603338820466037", + "delegation_rate": 1, + "vesting_schedule": { + "initial_release_timestamp_millis": 1719301233872, + "locked_amounts": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0" + ] + }, + "delegators": [ + { + "delegator_public_key": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", + "delegator": { + "delegator_public_key": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", + "staked_amount": "364612699495350744", + "bonding_purse": "uref-3ca8c2afabe6120c722f260f53b3dd406178b5341760618ed98f826d9d9aac3d-007", + "validator_public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "vesting_schedule": { + "initial_release_timestamp_millis": 1719301233872, + "locked_amounts": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0" + ] + } + } + } + ], + "inactive": false + } + }, + { + "public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "bid": { + "validator_public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "bonding_purse": "uref-5c654b8a6f9d0130770c37e6dddca477d02424d718aed53d0315f163c2591180-007", + "staked_amount": "712016868136394248", + "delegation_rate": 1, + "vesting_schedule": { + "initial_release_timestamp_millis": 1719301233872, + "locked_amounts": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0" + ] + }, + "delegators": [ + { + "delegator_public_key": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", + "delegator": { + "delegator_public_key": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", + "staked_amount": "49664621804232", + "bonding_purse": "uref-9120257acebbf71672c3397a08b18374dd995e7e176c7faf9fd0646c2d5a0cdf-007", + "validator_public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "vesting_schedule": null + } + }, + { + "delegator_public_key": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55", + "delegator": { + "delegator_public_key": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55", + "staked_amount": "654063155243124749", + "bonding_purse": "uref-64d20811fbd63205cb2267c01b2531198fbeeec69cf1c2611664553cc44bb621-007", + "validator_public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "vesting_schedule": { + "initial_release_timestamp_millis": 1719301233872, + "locked_amounts": [ + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0" + ] + } + } + }, + { + "delegator_public_key": "018b46617b2b97e633b36530f2964b3f4c15916235910a2737e83d4fa2c7fad542", + "delegator": { + "delegator_public_key": "018b46617b2b97e633b36530f2964b3f4c15916235910a2737e83d4fa2c7fad542", + "staked_amount": "51745006049231", + "bonding_purse": "uref-a5e8d8ad4ad983dd01270dcf1c7d3d2112ab48b55e2a0347fbbb51066fd686fc-007", + "validator_public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "vesting_schedule": null + } + } + ], + "inactive": false + } + } + ] + } +} diff --git a/Casper.Network.SDK.Test/TestData/get-deploy-result-version1-failure.json b/Casper.Network.SDK.Test/TestData/get-deploy-result-version1-failure.json new file mode 100644 index 0000000..7baf554 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-deploy-result-version1-failure.json @@ -0,0 +1,245 @@ +{ + "api_version": "2.0.0", + "deploy": { + "hash": "f21957f06b05720988672e625d9900f4d787524d05e4dbb30b04db18625aeac7", + "header": { + "account": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", + "timestamp": "2024-06-28T10:22:35.711Z", + "ttl": "3m", + "gas_price": 1, + "body_hash": "3cd24b57053a21605a0569edcd0da556782db67d5ee0529dedecc9e1271d34d6", + "dependencies": [], + "chain_name": "casper-net-1" + }, + "payment": { + "ModuleBytes": { + "module_bytes": "", + "args": [ + [ + "amount", + { + "cl_type": "U512", + "bytes": "0500f2052a01", + "parsed": "5000000000" + } + ] + ] + } + }, + "session": { + "StoredVersionedContractByHash": { + "hash": "2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "version": null, + "entry_point": "transfer", + "args": [ + [ + "recipient", + { + "cl_type": "Key", + "bytes": "006bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a", + "parsed": "account-hash-6bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a" + } + ], + [ + "amount", + { + "cl_type": "U256", + "bytes": "02d430", + "parsed": "12500" + } + ] + ] + } + }, + "approvals": [ + { + "signer": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", + "signature": "0188dd1e8400c8515c42df0184e11316b6eee94194a9be41b9bee9b4ce48304643a5bbb7cf5511649597f46c41c7a543882a09900244f65b82abad2bf906e9b20b" + } + ] + }, + "execution_info": { + "block_hash": "fee17570df0ee6c77ba19cc4194490f1f939ba7823a6a3e664c5599dc7067879", + "block_height": 122, + "execution_result": { + "Version1": { + "Failure": { + "effect": { + "operations": [], + "transforms": [ + { + "key": "account-hash-6174cf2e6f8fed1715c9a3bace9c50bfe572eecb763b0ed3f644532616452008", + "transform": "Identity" + }, + { + "key": "hash-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-d6513eb8e21953badd438f0e3cb01fa4c47f879cd6ecd28a25bd327cb0d0a0e2", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "hash-1499242da9799e647edf8e0903e75776fe5c1df56a26de15d028422ef1f2bf93", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "balance-f34ede14ef9d3adc031b37ae4473f6818f289b786083ce589343cf52a8f59841", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "balance-f34ede14ef9d3adc031b37ae4473f6818f289b786083ce589343cf52a8f59841", + "transform": { + "WriteCLValue": { + "cl_type": "U512", + "bytes": "0e000efad5085bc138938d44c64d31", + "parsed": "999999999999999999999995000000000" + } + } + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": { + "AddUInt512": "5000000000" + } + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-d6513eb8e21953badd438f0e3cb01fa4c47f879cd6ecd28a25bd327cb0d0a0e2", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "hash-1499242da9799e647edf8e0903e75776fe5c1df56a26de15d028422ef1f2bf93", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "balance-f34ede14ef9d3adc031b37ae4473f6818f289b786083ce589343cf52a8f59841", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": { + "WriteCLValue": { + "cl_type": "U512", + "bytes": "045419d004", + "parsed": "80746836" + } + } + }, + { + "key": "balance-f34ede14ef9d3adc031b37ae4473f6818f289b786083ce589343cf52a8f59841", + "transform": { + "AddUInt512": "4919253164" + } + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "hash-1499242da9799e647edf8e0903e75776fe5c1df56a26de15d028422ef1f2bf93", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "balance-59621579e9cd67523b65d2ac87fe05993be7dfd6ba677fd1b4200e64a53f9d16", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": { + "WriteCLValue": { + "cl_type": "U512", + "bytes": "00", + "parsed": "0" + } + } + }, + { + "key": "balance-59621579e9cd67523b65d2ac87fe05993be7dfd6ba677fd1b4200e64a53f9d16", + "transform": { + "AddUInt512": "80746836" + } + } + ] + }, + "transfers": [], + "cost": "31057410", + "error_message": "User error: 60001" + } + } + } + } +} + + diff --git a/Casper.Network.SDK.Test/TestData/get-deploy-result-version1-success.json b/Casper.Network.SDK.Test/TestData/get-deploy-result-version1-success.json new file mode 100644 index 0000000..4ce6f3d --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-deploy-result-version1-success.json @@ -0,0 +1,632 @@ +{ + "api_version": "2.0.0", + "deploy": { + "hash": "cdbb52d946ce58dccde35ac17a5820c0ee6bdddcf80d0e5e22572d2be6bffed6", + "header": { + "account": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55", + "timestamp": "2024-06-28T10:15:36.909Z", + "ttl": "30m", + "gas_price": 1, + "body_hash": "467d34798eb4075f9518a82f7a8eb959290f00ac5fb305c3428f63508082ba39", + "dependencies": [], + "chain_name": "casper-net-1" + }, + "payment": { + "ModuleBytes": { + "module_bytes": "", + "args": [ + [ + "amount", + { + "cl_type": "U512", + "bytes": "050088526a74", + "parsed": "500000000000" + } + ] + ] + } + }, + "session": { + "ModuleBytes": { + "module_bytes": "0061736d0100000141073602b803200141b8036a10ee808080002102200141b8036a0002100000", + "args": [ + [ + "name", + { + "cl_type": "String", + "bytes": "0b000000434c49434b542054657374", + "parsed": "CLICKT Test" + } + ], + [ + "symbol", + { + "cl_type": "String", + "bytes": "06000000434c49434b54", + "parsed": "CLICKT" + } + ], + [ + "decimals", + { + "cl_type": "U8", + "bytes": "03", + "parsed": 3 + } + ], + [ + "total_supply", + { + "cl_type": "U256", + "bytes": "0400ca9a3b", + "parsed": "1000000000" + } + ], + [ + "events_mode", + { + "cl_type": "U8", + "bytes": "01", + "parsed": 1 + } + ], + [ + "enable_mint_burn", + { + "cl_type": "U8", + "bytes": "00", + "parsed": 0 + } + ] + ] + } + }, + "approvals": [ + { + "signer": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55", + "signature": "01cddbf5fc583f7ebf2293837e8f306ecb3650ad8df3760010048e3a7ad75640dcce3e5b1c990cd596f60b01570fb963514c0af083a29b81194f708a777f15910b" + } + ] + }, + "execution_info": { + "block_hash": "63dcce7f471030454da37a84e35c62fac863ee73aea0a9bf159e973ecc3e2e7d", + "block_height": 23, + "execution_result": { + "Version1": { + "Success": { + "effect": { + "operations": [], + "transforms": [ + { + "key": "account-hash-6174cf2e6f8fed1715c9a3bace9c50bfe572eecb763b0ed3f644532616452008", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-d6513eb8e21953badd438f0e3cb01fa4c47f879cd6ecd28a25bd327cb0d0a0e2", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "hash-1499242da9799e647edf8e0903e75776fe5c1df56a26de15d028422ef1f2bf93", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "balance-bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "balance-bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e", + "transform": { + "WriteCLValue": { + "cl_type": "U512", + "bytes": "0e0078ad95955ac138938d44c64d31", + "parsed": "999999999999999999999500000000000" + } + } + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": { + "AddUInt512": "500000000000" + } + }, + { + "key": "uref-db1891fef9155d6ef077d8a93d5e3edcba5eea0d8298163fb9872e133268711e-000", + "transform": { + "WriteCLValue": { + "cl_type": "String", + "bytes": "0b000000434c49434b542054657374", + "parsed": "CLICKT Test" + } + } + }, + { + "key": "uref-e8d05c77d4bf5d46001bad5828c27d90b5da6e4a151496e46af1cc706efd48dd-000", + "transform": { + "WriteCLValue": { + "cl_type": "String", + "bytes": "06000000434c49434b54", + "parsed": "CLICKT" + } + } + }, + { + "key": "uref-21fe1e848187fdd960698ad8b7986d5c0e08de270b6757e7635c9d86e44e1f9e-000", + "transform": { + "WriteCLValue": { + "cl_type": "U8", + "bytes": "03", + "parsed": 3 + } + } + }, + { + "key": "uref-18556a7a9e30862aa6f649f184febae6edf1ffe038403ef85a8ff78ad97307b9-000", + "transform": { + "WriteCLValue": { + "cl_type": "U256", + "bytes": "0400ca9a3b", + "parsed": "1000000000" + } + } + }, + { + "key": "uref-3d67308514d1a0c447a07f088b165d54aa13f601f9e58791e059bc6344cd84f9-000", + "transform": { + "WriteCLValue": { + "cl_type": "U8", + "bytes": "01", + "parsed": 1 + } + } + }, + { + "key": "uref-5c12c6fa083d7a2340379cb9a56d07915f186d76624c7223c478a679962cecf4-000", + "transform": { + "WriteCLValue": { + "cl_type": "U8", + "bytes": "00", + "parsed": 0 + } + } + }, + { + "key": "uref-783e3cbee2b8acb3e58da315fdfdc809faf98f0c03d4de7b7d3fd1ec10a7bd6e-000", + "transform": { + "WriteCLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + }, + { + "key": "hash-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "transform": "WriteContractPackage" + }, + { + "key": "account-hash-56befc13a6fd62e18f361700a5e08f966901c34df8041b36ec97d54d605c23de", + "transform": { + "AddKeys": [ + { + "name": "cep18_contract_package_CLICKT Test", + "key": "hash-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb" + } + ] + } + }, + { + "key": "account-hash-56befc13a6fd62e18f361700a5e08f966901c34df8041b36ec97d54d605c23de", + "transform": { + "AddKeys": [ + { + "name": "cep18_contract_package_access_CLICKT Test", + "key": "uref-783e3cbee2b8acb3e58da315fdfdc809faf98f0c03d4de7b7d3fd1ec10a7bd6e-007" + } + ] + } + }, + { + "key": "hash-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "transform": "Identity" + }, + { + "key": "hash-906aa4db02430ae0894f6996c3dcb423013d9878037ad84a46c6f6ac78b7d5b1", + "transform": "WriteContractWasm" + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": "WriteContract" + }, + { + "key": "hash-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "transform": "WriteContractPackage" + }, + { + "key": "account-hash-56befc13a6fd62e18f361700a5e08f966901c34df8041b36ec97d54d605c23de", + "transform": { + "AddKeys": [ + { + "name": "cep18_contract_hash_CLICKT Test", + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01" + } + ] + } + }, + { + "key": "uref-09377f91048ec8d1b15df45c151e55683d8b93a779c5f2cef634d3ca1543b192-000", + "transform": { + "WriteCLValue": { + "cl_type": "U32", + "bytes": "01000000", + "parsed": 1 + } + } + }, + { + "key": "account-hash-56befc13a6fd62e18f361700a5e08f966901c34df8041b36ec97d54d605c23de", + "transform": { + "AddKeys": [ + { + "name": "cep18_contract_version_CLICKT Test", + "key": "uref-09377f91048ec8d1b15df45c151e55683d8b93a779c5f2cef634d3ca1543b192-007" + } + ] + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": "Identity" + }, + { + "key": "hash-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "transform": "Identity" + }, + { + "key": "hash-906aa4db02430ae0894f6996c3dcb423013d9878037ad84a46c6f6ac78b7d5b1", + "transform": "Identity" + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "package_hash", + "key": "hash-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb" + } + ] + } + }, + { + "key": "uref-ae50771df34a40472b425ddd98c874a0836888f3bd22e01842f7bfddb3549c6f-000", + "transform": { + "WriteCLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "allowances", + "key": "uref-ae50771df34a40472b425ddd98c874a0836888f3bd22e01842f7bfddb3549c6f-007" + } + ] + } + }, + { + "key": "uref-aeae29762c24430dd4e6dd0ff93994abd53c43907103a27bfe7d3881fdcebbc0-000", + "transform": { + "WriteCLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "balances", + "key": "uref-aeae29762c24430dd4e6dd0ff93994abd53c43907103a27bfe7d3881fdcebbc0-007" + } + ] + } + }, + { + "key": "dictionary-3242adf99a374c325bbc98d29323805a5a0c7d81c0b55731027111bc1b220869", + "transform": { + "WriteCLValue": { + "cl_type": "Any", + "bytes": "050000000400ca9a3b0720000000aeae29762c24430dd4e6dd0ff93994abd53c43907103a27bfe7d3881fdcebbc02c0000004146612b2f424f6d2f574c686a7a5958414b58676a355a7041634e4e2b4151624e7579583155316758435065", + "parsed": null + } + } + }, + { + "key": "uref-304e0283e0bd6fbb04dd97af1c6caea28cbd7a8937fcf46724052142cfe3f603-000", + "transform": { + "WriteCLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "security_badges", + "key": "uref-304e0283e0bd6fbb04dd97af1c6caea28cbd7a8937fcf46724052142cfe3f603-007" + } + ] + } + }, + { + "key": "dictionary-fbde437d8e6d72b1eecd9e776d5a54c70216381905dca0ef5e589cf6d708d5d1", + "transform": { + "WriteCLValue": { + "cl_type": "Any", + "bytes": "01000000000320000000304e0283e0bd6fbb04dd97af1c6caea28cbd7a8937fcf46724052142cfe3f6032c0000004146612b2f424f6d2f574c686a7a5958414b58676a355a7041634e4e2b4151624e7579583155316758435065", + "parsed": null + } + } + }, + { + "key": "uref-3d67308514d1a0c447a07f088b165d54aa13f601f9e58791e059bc6344cd84f9-000", + "transform": "Identity" + }, + { + "key": "uref-83fe09fd54cc79ce67e513bdef50297936fdcef11fa7886f984a373aa2fa000c-000", + "transform": { + "WriteCLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "__events", + "key": "uref-83fe09fd54cc79ce67e513bdef50297936fdcef11fa7886f984a373aa2fa000c-007" + } + ] + } + }, + { + "key": "uref-c241213422d3c9adb0afa8437ea3dab8c69dd863c0cf44536d7586538545b482-000", + "transform": { + "WriteCLValue": { + "cl_type": "U32", + "bytes": "00000000", + "parsed": 0 + } + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "__events_length", + "key": "uref-c241213422d3c9adb0afa8437ea3dab8c69dd863c0cf44536d7586538545b482-007" + } + ] + } + }, + { + "key": "uref-8ed3033a37c622f23a4c1689c29a3b773bec3a8ac8a583e866c3d196a7b2e8a4-000", + "transform": { + "WriteCLValue": { + "cl_type": { + "Map": { + "key": "String", + "value": { + "List": { + "Tuple2": [ + "String", + "Any" + ] + } + } + } + }, + "bytes": "08000000040000004275726e02000000050000006f776e65720b06000000616d6f756e74070e0000004368616e67655365637572697479020000000500000061646d696e0b0e0000007365635f6368616e67655f6d6170110b03110000004465637265617365416c6c6f77616e636504000000050000006f776e65720b070000007370656e6465720b09000000616c6c6f77616e63650707000000646563725f62790711000000496e637265617365416c6c6f77616e636504000000050000006f776e65720b070000007370656e6465720b09000000616c6c6f77616e63650706000000696e635f627907040000004d696e740200000009000000726563697069656e740b06000000616d6f756e74070c000000536574416c6c6f77616e636503000000050000006f776e65720b070000007370656e6465720b09000000616c6c6f77616e636507080000005472616e73666572030000000600000073656e6465720b09000000726563697069656e740b06000000616d6f756e74070c0000005472616e7366657246726f6d04000000070000007370656e6465720b050000006f776e65720b09000000726563697069656e740b06000000616d6f756e7407", + "parsed": null + } + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "__events_schema", + "key": "uref-8ed3033a37c622f23a4c1689c29a3b773bec3a8ac8a583e866c3d196a7b2e8a4-007" + } + ] + } + }, + { + "key": "uref-6533dbce8d122ee0310a51dc4416e1a594277ec8625f1df54cbc1dcccae27c4b-000", + "transform": { + "WriteCLValue": { + "cl_type": "String", + "bytes": "03000000312e31", + "parsed": "1.1" + } + } + }, + { + "key": "hash-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "transform": { + "AddKeys": [ + { + "name": "__events_ces_version", + "key": "uref-6533dbce8d122ee0310a51dc4416e1a594277ec8625f1df54cbc1dcccae27c4b-007" + } + ] + } + }, + { + "key": "deploy-cdbb52d946ce58dccde35ac17a5820c0ee6bdddcf80d0e5e22572d2be6bffed6", + "transform": { + "WriteDeployInfo": { + "deploy_hash": "cdbb52d946ce58dccde35ac17a5820c0ee6bdddcf80d0e5e22572d2be6bffed6", + "transfers": [], + "from": "account-hash-56befc13a6fd62e18f361700a5e08f966901c34df8041b36ec97d54d605c23de", + "source": "uref-bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e-007", + "gas": "239918433115" + } + } + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "hash-d6513eb8e21953badd438f0e3cb01fa4c47f879cd6ecd28a25bd327cb0d0a0e2", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "hash-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "transform": "Identity" + }, + { + "key": "account-hash-56befc13a6fd62e18f361700a5e08f966901c34df8041b36ec97d54d605c23de", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "hash-1499242da9799e647edf8e0903e75776fe5c1df56a26de15d028422ef1f2bf93", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "balance-bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": { + "WriteCLValue": { + "cl_type": "U512", + "bytes": "05900f467738", + "parsed": "242519248784" + } + } + }, + { + "key": "balance-bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e", + "transform": { + "AddUInt512": "257480751216" + } + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "hash-1499242da9799e647edf8e0903e75776fe5c1df56a26de15d028422ef1f2bf93", + "transform": "Identity" + }, + { + "key": "hash-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": "Identity" + }, + { + "key": "balance-59621579e9cd67523b65d2ac87fe05993be7dfd6ba677fd1b4200e64a53f9d16", + "transform": "Identity" + }, + { + "key": "balance-a1e58c31ab6ef541735d5a13ee768c72031b5042c3b128e45119cf1087b3c93d", + "transform": { + "WriteCLValue": { + "cl_type": "U512", + "bytes": "00", + "parsed": "0" + } + } + }, + { + "key": "balance-59621579e9cd67523b65d2ac87fe05993be7dfd6ba677fd1b4200e64a53f9d16", + "transform": { + "AddUInt512": "242519248784" + } + } + ] + }, + "transfers": [], + "cost": "239918433115" + } + } + } + } +} + + diff --git a/Casper.Network.SDK.Test/TestData/get-deploy-result-version2-success.json b/Casper.Network.SDK.Test/TestData/get-deploy-result-version2-success.json new file mode 100644 index 0000000..6b31829 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-deploy-result-version2-success.json @@ -0,0 +1,225 @@ +{ + "api_version": "2.0.0", + "deploy": { + "hash": "8738a51ca5c656e1c3eea5d4473fd07f92d6bb7dfd2d75d8d8dc59331545096c", + "header": { + "account": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55", + "timestamp": "2024-06-28T13:05:19.722Z", + "ttl": "3m", + "gas_price": 1, + "body_hash": "3cd24b57053a21605a0569edcd0da556782db67d5ee0529dedecc9e1271d34d6", + "dependencies": [], + "chain_name": "casper-net-1" + }, + "payment": { + "ModuleBytes": { + "module_bytes": "", + "args": [ + [ + "amount", + { + "cl_type": "U512", + "bytes": "0500f2052a01", + "parsed": "5000000000" + } + ] + ] + } + }, + "session": { + "StoredVersionedContractByHash": { + "hash": "2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "version": null, + "entry_point": "transfer", + "args": [ + [ + "recipient", + { + "cl_type": "Key", + "bytes": "006bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a", + "parsed": "account-hash-6bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a" + } + ], + [ + "amount", + { + "cl_type": "U256", + "bytes": "02d430", + "parsed": "12500" + } + ] + ] + } + }, + "approvals": [ + { + "signer": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55", + "signature": "01c5caded9fd67da7232ad55b1fe3f6781ee5212c3c71b755866d63543d014a2501dece11e3f3c1073b919dcaf41b09d1709b3da7caae07bbbb4eb2d01876c7c0a" + } + ] + }, + "execution_info": { + "block_hash": "638d6a36843f153f173810569ca41129228d49e9f786bb96cd59c0215c2101b6", + "block_height": 1964, + "execution_result": { + "Version2": { + "initiator": { + "PublicKey": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55" + }, + "error_message": null, + "limit": "500000000000", + "consumed": "537061384", + "cost": "500000000000", + "payment": [], + "transfers": [], + "size_estimate": 392, + "effects": [ + { + "key": "balance-hold-01bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e1d4ef35e90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "050088526a74", + "parsed": "500000000000" + } + } + } + }, + { + "key": "package-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "kind": "Identity" + }, + { + "key": "entity-contract-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01", + "kind": "Identity" + }, + { + "key": "package-2f5f35e499d33f7bbb03f6657c12c3bee0ca2eebd9cfe177fb5cc2dbb6378feb", + "kind": "Identity" + }, + { + "key": "entry-point-v1-entity-contract-3ec4d7e8ec755ba06be9ee21dd0ffa69028fb92fbe4316e880bb417a13898d01-3820ce25e54df0470fb738e3e0f63ee50b2719cf2680da2bdb579e21aebc8f63", + "kind": "Identity" + }, + { + "key": "byte-code-v1-wasm-906aa4db02430ae0894f6996c3dcb423013d9878037ad84a46c6f6ac78b7d5b1", + "kind": "Identity" + }, + { + "key": "dictionary-3242adf99a374c325bbc98d29323805a5a0c7d81c0b55731027111bc1b220869", + "kind": "Identity" + }, + { + "key": "dictionary-5f00faae08f256fdbbcab53942ae89f0961dbc28142c49d3f6fc7efb827887cf", + "kind": "Identity" + }, + { + "key": "dictionary-3242adf99a374c325bbc98d29323805a5a0c7d81c0b55731027111bc1b220869", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "050000000484379a3b0720000000aeae29762c24430dd4e6dd0ff93994abd53c43907103a27bfe7d3881fdcebbc02c0000004146612b2f424f6d2f574c686a7a5958414b58676a355a7041634e4e2b4151624e7579583155316758435065", + "parsed": null + } + } + } + }, + { + "key": "dictionary-5f00faae08f256fdbbcab53942ae89f0961dbc28142c49d3f6fc7efb827887cf", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "03000000027c920720000000aeae29762c24430dd4e6dd0ff93994abd53c43907103a27bfe7d3881fdcebbc02c0000004147764d746b7035424c636866645474665a35425933686634684d3952644f514a5173592b5551704637774b", + "parsed": null + } + } + } + }, + { + "key": "uref-3d67308514d1a0c447a07f088b165d54aa13f601f9e58791e059bc6344cd84f9-000", + "kind": "Identity" + }, + { + "key": "uref-c241213422d3c9adb0afa8437ea3dab8c69dd863c0cf44536d7586538545b482-000", + "kind": "Identity" + }, + { + "key": "dictionary-b8404730e4b5686f96941958a9aabab4ee4997b2333cee681e96ed986d159107", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "5b000000570000000e0000006576656e745f5472616e736665720056befc13a6fd62e18f361700a5e08f966901c34df8041b36ec97d54d605c23de006bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a02d4300e032000000083fe09fd54cc79ce67e513bdef50297936fdcef11fa7886f984a373aa2fa000c0100000032", + "parsed": null + } + } + } + }, + { + "key": "uref-c241213422d3c9adb0afa8437ea3dab8c69dd863c0cf44536d7586538545b482-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U32", + "bytes": "03000000", + "parsed": 3 + } + } + } + }, + { + "key": "balance-hold-01bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e1d4ef35e90010000", + "kind": { + "Prune": "balance-hold-01bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e1d4ef35e90010000" + } + }, + { + "key": "balance-hold-00bc174dc2398daae9d676470292db5f34c5c4aafb059ee08543e1d725667a7c1e1d4ef35e90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "050088526a74", + "parsed": "500000000000" + } + } + } + }, + { + "key": "entity-system-96d9230e1a4e2154023d3d8f581a9f0503bae015907aec8debdac83054e3c019", + "kind": "Identity" + }, + { + "key": "entity-system-c3d73ae472263ea840378de7487c67b3b598dcd281d7851a429e783b25d4e959", + "kind": "Identity" + }, + { + "key": "entity-system-bd8cb7d0da04ad619b8981e0ffa13ea80c3fa6deb8c56fbff435e922e4d92774", + "kind": "Identity" + }, + { + "key": "bid-addr-011c50d14ca563d5afe0399a3da010bac01383f502bc7cce774cc03d858a94cf06", + "kind": "Identity" + }, + { + "key": "bid-addr-041c50d14ca563d5afe0399a3da010bac01383f502bc7cce774cc03d858a94cf06ba00000000000000", + "kind": { + "Write": { + "BidKind": { + "Credit": { + "validator_public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "era_id": 186, + "amount": "500000000000" + } + } + } + } + } + ] + } + } + } +} diff --git a/Casper.Network.SDK.Test/TestData/get-entity-account-v200.json b/Casper.Network.SDK.Test/TestData/get-entity-account-v200.json new file mode 100644 index 0000000..3bc214e --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-entity-account-v200.json @@ -0,0 +1,31 @@ +{ + "api_version": "2.0.0", + "entity": { + "AddressableEntity": { + "entity": { + "protocol_version": "2.0.0", + "entity_kind": { + "Account": "account-hash-65a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b1566213" + }, + "package_hash": "package-8117d677f8d08045b0db10c9efe309a801c39c3c41e86bc801883c6343fbe318", + "byte_code_hash": "byte-code-0000000000000000000000000000000000000000000000000000000000000000", + "main_purse": "uref-7decb5dcf734bace39f398bc19a3f371ac5ffe68c23f7fa8c2abaf7ae2edd1ad-007", + "associated_keys": [ + { + "account_hash": "account-hash-65a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b1566213", + "weight": 1 + } + ], + "action_thresholds": { + "deployment": 1, + "upgrade_management": 1, + "key_management": 1 + }, + "message_topics": [] + }, + "named_keys": [], + "entry_points": [] + } + }, + "merkle_proof": "01000000110165a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b15662130d8117d677f8d08045b0db10c9efe309a801c39c3c41e86bc801883c6343fbe31800000000000000000000000000000000000000000000000000000000000000000200000000000000000000007decb5dcf734bace39f398bc19a3f371ac5ffe68c23f7fa8c2abaf7ae2edd1ad070100000065a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b156621301010101000000000165a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b1566213030000000065140000001c004140e396af85425c4d13c428fff8c94a6f7bb5dec7fd618b4e23b3b58369721822007ab457a5e6805d0fdeb823c09ab40dff1d4941951ab0b2645861aeb8e4d6b2d02b00f0f334d6a39190dbc16682ec95db4553df44d11558952b172106082bac5e0aa52f00b7dacae2754a0970a96016fa199d5ad254701873fee1cb50436cd2288b5b83a13d01c02bcc20002435fa0760dfcae0f0b3d65909f0907169ef07fc33ceee4a93ff813e00379d62bdd28b3cc340a34484ad14325f06a21b3c34d15b45ed1b5ae23cdc70e15200f060fe324c53b5e1746c06d158b1919596c3ce71a5fe3b9ff115e137415f23d75300c1603481f9cd5dfa0d86f68321cc1cba0baf160efa55e19de1ca3516468617bc5600d31eb794b67f631b10b1f5372991c4ea7367fc05ccc8e4b2bdf147d1b353f7c3610095fc5e8e2f88cf31f67b30c74e507cbfbc3370750d98834204cca8d5f43e1b4a6b0032d4fcc51712e24553680440cb511aa1a91a1fa554c9135926eb3566931e2ace87008b6dd1ababcce7cc20691e6c0e87fef231b7c9c342a43ab7b2b935b39dd4848f8800c53304e96293c383d723e2d1ba7a2b0a3f6df67643f5c45363d19a88d561163f9f0017d4c50c86f70a2ea288b1deafe78eb99986a312e750f1de6c3d75daefb83d1dc20079f01d2ea83a6ea636b1c24e9ddc7ed3487a96c5f829ffc64a2a8f8f1dd00855cd001f40c6887abe655291469455b0d84c7cd58b74d896ed63e2b58d488906d054d8db00fabbb7ee9e93dcb60b3d4f70ab3c3fde9172dcdbf9ca90bd686b2632e73485afdc00e16ac8017d72f86f6b2c9fa52d6e177efa53c89c9d734ab1c64206e6005fd2eae200a13d5697094dbcef9b9bea3a8a59f0ce57a48445ade810658f7a127eb4f9e31dff00d60902a31f311443a0490c83cc48591c64da565901940683bc4cd43fea3a8d070001010000000001329fa50131086d2de5e18cdb8487b1e27a2e04ce7a574f185d22862019271fa100110d000000000106797c4406a0a246f352ba5592920ed0b3e214c688e080962ca74d36d7a1a99002012ec2b919aac0633305c4259420ce41a8b3a530e1b7dbbdce3c60c72cd1968c9506011ff8296cf60a3a7469992155c753f1dcb063a1db770cb5fe4e4f591d5ad421dd0a0029b220b5550023f51a78e121017aebf0089c690ab74adff840c31392ce2a59370b00a4c3b00132e152ef5fe45d3a7c56254b8b1651565b7c16e34a7b0abf6969ae820d0010fa1f5eb43fa8f8d7d286d8e5394ce7362095bd049788c3f8eb82f997e675240e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f013853ed3060a42737482e66540def898bdbf8534e7b24e9999a5af9fe6671ba0e100159f9269ebe85ad359217e196d018cacb941a04bb7f8e251f0c73435fc83c277c1200693fff58318fbd94674eb111fbc49ea8ab66ece1cd614b0008d387f228d778ee14015a38f15c34cc79868b4445af51125e24c100d0cf719b1eef5202458d74c146751500dd70cbafab73cb6edfb05345ed175bd858f2985199a9fa113e6d7710facf8bed17012e0cdc4b06694b367a198ff7f1676e8fd104c958d902b2e27b7117dd1572e234" +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/get-entity-contract-v200.json b/Casper.Network.SDK.Test/TestData/get-entity-contract-v200.json new file mode 100644 index 0000000..2420021 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-entity-contract-v200.json @@ -0,0 +1,385 @@ +{ + "api_version": "2.0.0", + "entity": { + "AddressableEntity": { + "entity": { + "protocol_version": "2.0.0", + "entity_kind": { + "SmartContract": "VmCasperV1" + }, + "package_hash": "package-7a25b7c52f6c69b8d229ae50c821c5edf4e0c2bd3ac73ec52386ed42440ce5c6", + "byte_code_hash": "byte-code-85def61e3ee02e10a1e845cfb8e8b2d9640a18f605333158027a24ed8569d895", + "main_purse": "uref-d3df027aba457c3e0a657148427f90c5a6fcdbe33b0c841ee91aacd63838cbba-007", + "associated_keys": [ + { + "account_hash": "account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4", + "weight": 1 + } + ], + "action_thresholds": { + "deployment": 1, + "upgrade_management": 1, + "key_management": 1 + }, + "message_topics": [ + { + "topic_name": "events", + "topic_name_hash": "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1" + } + ] + }, + "named_keys": [ + { + "name": "allowances", + "key": "uref-2573b5cca61e31f7b602f135a9abbe096367f5c07f593d006ba10134d3698aa4-007" + }, + { + "name": "balances", + "key": "uref-96cd0453fb2e1d063c9438c158c0d804d0121a96f3423046150ee355cfadefb6-007" + }, + { + "name": "contract_hash", + "key": "entity-contract-9bd4c534c8055bc82e44a9f8c02b485008ee28108f79c8556b57dc9f5a8ef63d" + }, + { + "name": "decimals", + "key": "uref-da8081ab755640837c90d9088579f9d7e29c09c3ba0295f59b9ee166ba0bf374-007" + }, + { + "name": "enable_mint_burn", + "key": "uref-cc1accfee71597d884d2e22f7cb166f53cdf001e568a6d32ed74877d80472fca-007" + }, + { + "name": "events_mode", + "key": "uref-c537df8ce12730db7a9b74f5338b2121751d5813c65e648b7bba9a6223f5353c-007" + }, + { + "name": "name", + "key": "uref-bd0fc79a60c201f454f6840e3ed2a9f81f1ea55a7d5f59b08df5f0450b5dc9e6-007" + }, + { + "name": "package_hash", + "key": "package-7a25b7c52f6c69b8d229ae50c821c5edf4e0c2bd3ac73ec52386ed42440ce5c6" + }, + { + "name": "security_badges", + "key": "uref-f0ac8d1883c4be33bf902b44f1227b6f1fccfcb9bfa491eb13ab2d18ffeeca5e-007" + }, + { + "name": "symbol", + "key": "uref-5dde41a496dd0b1c55b44ca8fbeeb4d17669a019067533ec0ace3571c9b82b8b-007" + }, + { + "name": "total_supply", + "key": "uref-007bc04abf7d45a8e2748373899ead7691fe5349d9bcd5d95740d602a9245c4b-007" + } + ], + "entry_points": [ + { + "V1CasperVm": { + "name": "migrate_user_allowance_keys", + "args": [ + { + "name": "events", + "cl_type": "Bool" + }, + { + "name": "revert", + "cl_type": "Bool" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "allowance", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "spender", + "cl_type": "Key" + } + ], + "ret": "U256", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "migrate_user_balance_keys", + "args": [ + { + "name": "events", + "cl_type": "Bool" + }, + { + "name": "revert", + "cl_type": "Bool" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "mint", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "transfer", + "args": [ + { + "name": "recipient", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "transfer_from", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "recipient", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "approve", + "args": [ + { + "name": "spender", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "init", + "args": [], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "name", + "args": [], + "ret": "String", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "burn", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "change_security", + "args": [], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "increase_allowance", + "args": [ + { + "name": "spender", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "decrease_allowance", + "args": [ + { + "name": "spender", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "change_events_mode", + "args": [ + { + "name": "events_mode", + "cl_type": "U8" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "decimals", + "args": [], + "ret": "U8", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "symbol", + "args": [], + "ret": "String", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "total_supply", + "args": [], + "ret": "U256", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "migrate_sec_keys", + "args": [ + { + "name": "events", + "cl_type": "Bool" + }, + { + "name": "revert", + "cl_type": "Bool" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + }, + { + "V1CasperVm": { + "name": "balance_of", + "args": [ + { + "name": "address", + "cl_type": "Key" + } + ], + "ret": "U256", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + ] + } + }, + "merkle_proof": "0100000011029bd4c534c8055bc82e44a9f8c02b485008ee28108f79c8556b57dc9f5a8ef63d0d7a25b7c52f6c69b8d229ae50c821c5edf4e0c2bd3ac73ec52386ed42440ce5c685def61e3ee02e10a1e845cfb8e8b2d9640a18f605333158027a24ed8569d895020000000000000000000000d3df027aba457c3e0a657148427f90c5a6fcdbe33b0c841ee91aacd63838cbba07010000009fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd40101010101000000060000006576656e74735721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e10200020000000002020000000001329fa50131086d2de5e18cdb8487b1e27a2e04ce7a574f185d22862019271fa1010109b9c1523ad187123214b99094869646952ecf4f56f83c168adb43c690f8c7090011100000000001e9602fcc1c2dd571ad440b6157fb44ae056257301ae8ebbe64ed156289a18ed2020161620e026b0fb697da2be845771ddadeaa555a8c421b5c06f1c2209f582cda8c060166119f72425746e2133a0a8d67dfd34cd0d89db2e86e37a112c5089bf5b9524e09011d429bda3bf002784d0ea8ba8cd94ca26534c2102a088daeece5f28608a6ad240a0029b220b5550023f51a78e121017aebf0089c690ab74adff840c31392ce2a59370b00a6503cbee1ef52651ad311cd7d32d727d1c5349afa428b0109ae30e0e69aed2f0d0010fa1f5eb43fa8f8d7d286d8e5394ce7362095bd049788c3f8eb82f997e675240e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f015ba4f65f5b54cab5eab7171a7218f09c4cb20cf3a5127b7491ff9230b1ecf2ea10016997f28e86b4a18d9f74c494e44cc58a3aecec712e5f332917b6ec751f7a42711201dc991a88ba67859da4efb3d13273933b7d6e2e1cd3826e81a966b967c8cf54ee1301cfce4655aaa43c05dc69a3ebb887808e9d04b01d9c4bff5913f80c1136b83ffd1401c0956fb9a4815604a8e12528088273a2bdc7961cdc7480a6fa1c54bad136fb501501f912293449cf99294a99e4d774b1c2f8fb7f8760cd9c5625a60de980de7c3e381601483cc5eb620c842e555ffa3b69feff61910c79740010b80f8d30d6bc73d6b06417012e40a254b1d0a5136f9713b4af327548efed7ff2d2177446f14c57c103b4c4b9" +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/sse-transaction-processed-v200.json b/Casper.Network.SDK.Test/TestData/sse-transaction-processed-v200.json new file mode 100644 index 0000000..96a5b0d --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/sse-transaction-processed-v200.json @@ -0,0 +1,202 @@ +{ + "transaction_hash": { + "Version1": "9749ce1dc5dbd0d9a611088f934fd81c2c8429dbab0a3a7d281359be0a92d29a" + }, + "initiator_addr": { + "PublicKey": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a" + }, + "timestamp": "2024-07-01T15:24:47.381Z", + "ttl": "30m", + "block_hash": "b3fc60731e800dedfc60fcb1d85b91a93cf688237e53d2f8920e4fad4ab047ee", + "execution_result": { + "Version2": { + "initiator": { + "PublicKey": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a" + }, + "error_message": null, + "limit": "100000000000", + "consumed": "520224703", + "cost": "100000000000", + "payment": [], + "transfers": [], + "size_estimate": 366, + "effects": [ + { + "key": "balance-hold-01a9f3707fb752513760d089f4625a8727ba1e8b44880a8180e3780c29003742975c0ee66e90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "0500e8764817", + "parsed": "100000000000" + } + } + } + }, + { + "key": "package-257b05da64d656592cde44bf3aa9f70f97b377b6342e016234d90aaaaaf0cfc5", + "kind": "Identity" + }, + { + "key": "entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed", + "kind": "Identity" + }, + { + "key": "package-257b05da64d656592cde44bf3aa9f70f97b377b6342e016234d90aaaaaf0cfc5", + "kind": "Identity" + }, + { + "key": "entry-point-v1-entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed-3820ce25e54df0470fb738e3e0f63ee50b2719cf2680da2bdb579e21aebc8f63", + "kind": "Identity" + }, + { + "key": "byte-code-v1-wasm-9e011083366d6dd0c32fafdf2b2ed5fa021b9c1c001af4c25b76715d6ef026af", + "kind": "Identity" + }, + { + "key": "dictionary-83e84f07db6e29c87ca51d44ac058f7ff61e0c1050eaf16110237b848048133f", + "kind": "Identity" + }, + { + "key": "dictionary-2d3fe9773442ce06ecb65e86a4a92d6818cb51cd6021218612a35250a7320298", + "kind": "Identity" + }, + { + "key": "dictionary-83e84f07db6e29c87ca51d44ac058f7ff61e0c1050eaf16110237b848048133f", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "0800000007aebcc5a47e8d0307200000002d5926cca6ebf4fd7d3555dca958f1fa3c6152418f89908515c062b61e7b665b30000000455147666f667749434e4f6c756571664f76544b6649773256566150337a654e69763334702b5675574b752f31413d3d", + "parsed": null + } + } + } + }, + { + "key": "dictionary-2d3fe9773442ce06ecb65e86a4a92d6818cb51cd6021218612a35250a7320298", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "030000000252c307200000002d5926cca6ebf4fd7d3555dca958f1fa3c6152418f89908515c062b61e7b665b300000004551482f716e4d7a46434865517631786f6f676b34574271484b663664552b52395148777a31594258334b457a513d3d", + "parsed": null + } + } + } + }, + { + "key": "uref-9561215a982d288e4d20426b9d45605b1642c7d0fe54c9f8632eb087b0a3dc7a-000", + "kind": "Identity" + }, + { + "key": "message-topic-entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "kind": "Identity" + }, + { + "key": "block-message-count-00000000000000000000000000000000000000000000000000000000000000", + "kind": "Identity" + }, + { + "key": "message-entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-0", + "kind": { + "Write": { + "Message": "message-checksum-4fa4135e65967751f007064a7cbf6c17d27f7189cc97b7b0f484445c72ecbd6f" + } + } + }, + { + "key": "message-topic-entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "kind": { + "Write": { + "MessageTopic": { + "message_count": 1, + "blocktime": 1719847489116 + } + } + } + }, + { + "key": "block-message-count-00000000000000000000000000000000000000000000000000000000000000", + "kind": { + "Write": { + "CLValue": { + "cl_type": { + "Tuple2": [ + "U64", + "U64" + ] + }, + "bytes": "5c0ee66e900100000100000000000000", + "parsed": [ + 1719847489116, + 1 + ] + } + } + } + }, + { + "key": "balance-hold-01a9f3707fb752513760d089f4625a8727ba1e8b44880a8180e3780c29003742975c0ee66e90010000", + "kind": { + "Prune": "balance-hold-01a9f3707fb752513760d089f4625a8727ba1e8b44880a8180e3780c29003742975c0ee66e90010000" + } + }, + { + "key": "balance-hold-00a9f3707fb752513760d089f4625a8727ba1e8b44880a8180e3780c29003742975c0ee66e90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "0500e8764817", + "parsed": "100000000000" + } + } + } + }, + { + "key": "entity-system-5a28db419f9dfc65ad39f6c221a7801ff16ee15b6140a6ae7eb96903544b4928", + "kind": "Identity" + }, + { + "key": "entity-system-3d4d648d49f1840fdbf82cb41dbec8b64b164ed17dfe0fea0d444bdd44f688d3", + "kind": "Identity" + }, + { + "key": "entity-system-ba7e4fcf24bdc36fc932881f4989e7df2b542e8e58700765a8155903b9ced06b", + "kind": "Identity" + }, + { + "key": "bid-addr-01dcd0c38c46c9d5c7083aa1a46b430e8e460f97f8f0bf8444776ac925187acfcc", + "kind": "Identity" + }, + { + "key": "bid-addr-04dcd0c38c46c9d5c7083aa1a46b430e8e460f97f8f0bf8444776ac925187acfcc0c00000000000000", + "kind": { + "Write": { + "BidKind": { + "Credit": { + "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "era_id": 12, + "amount": "100000000000" + } + } + } + } + } + ] + } + }, + "messages": [ + { + "entity_hash": "entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed", + "message": { + "String": "Transfer(Transfer { sender: Key::AddressableEntity(account-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4), recipient: Key::AddressableEntity(account-ffaa73331421de42fd71a28824e1606a1ca7fa754f91f501f0cf56015f7284cd), amount: 12502 })" + }, + "topic_name": "events", + "topic_name_hash": "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "topic_index": 1, + "block_index": 2 + } + ] +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TransferDeployTest.cs b/Casper.Network.SDK.Test/TransferDeployTest.cs index 1ef8f00..51b6e75 100644 --- a/Casper.Network.SDK.Test/TransferDeployTest.cs +++ b/Casper.Network.SDK.Test/TransferDeployTest.cs @@ -41,7 +41,7 @@ public void Transfer1() var deploy = new Deploy(header, payment, session); - deploy.AddApproval(new DeployApproval() + deploy.AddApproval(new Approval() { Signature = Signature.FromHexString("012dbf03817a51794a8e19e0724884075e6d1fbec326b766ecfa6658b41f81290da85e23b24e88b1c8d9761185c961daee1adab0649912a6477bcd2e69bd91bd08"), Signer = PublicKey.FromHexString("01027c04a0210afdf4a83328d57e8c2a12247a86d872fb53367f22a84b1b53d2a9") diff --git a/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs b/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs index 26698e1..3dff02b 100644 --- a/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs +++ b/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs @@ -44,5 +44,17 @@ protected static void WriteString(MemoryStream ms, string value) ms.Write(lenBytes); ms.Write(valueBytes); } + + protected static void WriteMaybeUInteger(MemoryStream ms, uint? maybeValue) + { + WriteByte(ms, (byte)(maybeValue.HasValue ? 0x01 : 0x00)); + + if (maybeValue.HasValue) + { + var bytes = BitConverter.GetBytes(maybeValue.Value); + if(!BitConverter.IsLittleEndian) Array.Reverse(bytes); + ms.Write(bytes); + } + } } } \ No newline at end of file diff --git a/Casper.Network.SDK/ByteSerializers/DeployApprovalByteSerializer.cs b/Casper.Network.SDK/ByteSerializers/DeployApprovalByteSerializer.cs index b9f62d4..d3e6762 100644 --- a/Casper.Network.SDK/ByteSerializers/DeployApprovalByteSerializer.cs +++ b/Casper.Network.SDK/ByteSerializers/DeployApprovalByteSerializer.cs @@ -3,9 +3,9 @@ namespace Casper.Network.SDK.ByteSerializers { - public class DeployApprovalByteSerializer : BaseByteSerializer, IByteSerializer + public class DeployApprovalByteSerializer : BaseByteSerializer, IByteSerializer { - public byte[] ToBytes(DeployApproval source) + public byte[] ToBytes(Approval source) { var ms = new MemoryStream(); WriteBytes(ms, source.Signer.GetBytes()); diff --git a/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs b/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs new file mode 100644 index 0000000..4b8b5cc --- /dev/null +++ b/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs @@ -0,0 +1,189 @@ +using System; +using System.IO; +using Casper.Network.SDK.Types; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Casper.Network.SDK.ByteSerializers +{ + public class TransactionV1ByteSerializer : BaseByteSerializer, IByteSerializer + { + public byte[] ToBytes(TransactionTarget source) + { + var ms = new MemoryStream(); + + WriteByte(ms, (byte)source.Type); + + if (source.Type == TransactionTargetType.Stored) + { + switch (source.Id) + { + case ByHashInvocationTarget byHash: + WriteByte(ms, (byte)InvocationTargetTag.ByHash); + WriteBytes(ms, Hex.Decode(byHash.Hash)); + break; + case ByNameInvocationTarget byName: + WriteByte(ms, (byte)InvocationTargetTag.ByName); + WriteString(ms, byName.Name); + break; + case ByPackageHashInvocationTarget byPackageHash: + WriteByte(ms, (byte)InvocationTargetTag.ByPackageHash); + WriteBytes(ms, Hex.Decode(byPackageHash.Addr)); + WriteMaybeUInteger(ms, byPackageHash.Version); + break; + case ByPackageNameInvocationTarget byPackageName: + WriteByte(ms, (byte)InvocationTargetTag.ByPackageName); + WriteString(ms, byPackageName.Name); + WriteMaybeUInteger(ms, byPackageName.Version); + break; + } + WriteByte(ms, (byte)source.Runtime); + } + else if (source.Type == TransactionTargetType.Session) + { + if (source.ModuleBytes == null || source.ModuleBytes.Length == 0) + WriteInteger(ms, 0); + else + { + WriteInteger(ms, source.ModuleBytes.Length); + WriteBytes(ms, source.ModuleBytes); + } + + WriteByte(ms, (byte)source.Runtime); + } + + return ms.ToArray(); + } + + public byte[] ToBytes(TransactionEntryPoint source) + { + var ms = new MemoryStream(); + + if (source.Custom != null) + { + WriteByte(ms, 0x00); + WriteString(ms, source.Custom); + } + else if (source.Native.HasValue) + { + WriteByte(ms, (byte)source.Native.Value); + } + else + { + throw new Exception("Cannot serialize empty TransactionEntryPoint to bytes"); + } + + return ms.ToArray(); + } + + public byte[] ToBytes(TransactionScheduling source) + { + var ms = new MemoryStream(); + + WriteByte(ms, (byte)source.Type); + + if (source.Type == TransactionSchedulingType.FutureEra) + WriteULong(ms, source.EraId); + if (source.Type == TransactionSchedulingType.FutureTimestamp) + WriteULong(ms, source.Timestamp); + + return ms.ToArray(); + } + + public byte[] ToBytes(TransactionV1Body source) + { + var ms = new MemoryStream(); + + var namedArgSerializer = new NamedArgByteSerializer(); + + ms.Write(BitConverter.GetBytes(source.RuntimeArgs.Count)); + foreach (var args in source.RuntimeArgs) + WriteBytes(ms, namedArgSerializer.ToBytes(args)); + + WriteBytes(ms, ToBytes(source.Target)); + WriteBytes(ms, ToBytes(source.EntryPoint)); + WriteByte(ms, source.TransactionCategory); + WriteBytes(ms, ToBytes(source.Scheduling)); + + return ms.ToArray(); + } + + public byte[] ToBytes(PricingMode source) + { + var ms = new MemoryStream(); + + if (source.Type == PricingModeType.Classic && + source.PaymentAmount.HasValue && + source.GasPriceTolerance.HasValue && + source.StandardPayment.HasValue) + { + ms.WriteByte((byte)PricingModeType.Classic); + WriteULong(ms, (ulong)source.PaymentAmount.Value); + WriteByte(ms, (byte)source.GasPriceTolerance.Value); + WriteByte(ms, (byte)(source.StandardPayment.Value ? 0x01 : 0x00)); + } + else if (source.Type == PricingModeType.Fixed && + source.GasPriceTolerance.HasValue) + { + ms.WriteByte((byte)PricingModeType.Fixed); + WriteByte(ms, (byte)source.GasPriceTolerance.Value); + } + else if (source.Type == PricingModeType.Reserved && + source.Receipt != null) + { + ms.WriteByte((byte)PricingModeType.Reserved); + WriteBytes(ms, Hex.Decode(source.Receipt)); + } + + return ms.ToArray(); + } + + public byte[] ToBytes(InitiatorAddr source) + { + var ms = new MemoryStream(); + + if (source.PublicKey != null) + { + WriteByte(ms, 0x00); + WriteBytes(ms, source.PublicKey.GetBytes()); + } + else if (source.AccountHash != null) + { + WriteByte(ms, 0x01); + WriteBytes(ms, source.AccountHash.RawBytes); + } + + return ms.ToArray(); + } + + public byte[] ToBytes(TransactionV1Header source) + { + var ms = new MemoryStream(); + WriteString(ms, source.ChainName); + WriteULong(ms, source.Timestamp); + WriteULong(ms, source.Ttl); + WriteBytes(ms, Hex.Decode(source.BodyHash)); + WriteBytes(ms, ToBytes(source.PricingMode)); + WriteBytes(ms, ToBytes(source.InitiatorAddr)); + return ms.ToArray(); + } + + public byte[] ToBytes(TransactionV1 source) + { + var ms = new MemoryStream(); + + WriteBytes(ms, Hex.Decode(source.Hash)); + + WriteBytes(ms, ToBytes(source.Header)); + + WriteBytes(ms, ToBytes(source.Body)); + // add the approvals + // + var approvalSerializer = new DeployApprovalByteSerializer(); + WriteInteger(ms, source.Approvals.Count); + foreach (var approval in source.Approvals) + WriteBytes(ms, approvalSerializer.ToBytes(approval)); + + return ms.ToArray(); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Casper.Network.SDK.csproj b/Casper.Network.SDK/Casper.Network.SDK.csproj index d2f46d6..ec45be5 100644 --- a/Casper.Network.SDK/Casper.Network.SDK.csproj +++ b/Casper.Network.SDK/Casper.Network.SDK.csproj @@ -24,7 +24,7 @@ - + diff --git a/Casper.Network.SDK/Converters/BidsListConverter.cs b/Casper.Network.SDK/Converters/BidsListConverter.cs index fc4cd35..03c981f 100644 --- a/Casper.Network.SDK/Converters/BidsListConverter.cs +++ b/Casper.Network.SDK/Converters/BidsListConverter.cs @@ -13,14 +13,14 @@ public override List Read( Type typeToConvert, JsonSerializerOptions options) { + var bids = new List(); + if (reader.TokenType != JsonTokenType.StartArray) - throw new JsonException("Array token expected to deserialize a list of Bids"); + throw new JsonException("StartArray token expected to deserialize a list of Bids"); reader.Read(); // Start array - List bids = new List(); - - while (reader.TokenType == JsonTokenType.StartObject) + while (reader.TokenType != JsonTokenType.EndArray) { reader.Read(); @@ -55,6 +55,7 @@ public override List Read( Inactive = bid.Inactive, StakedAmount = bid.StakedAmount, PublicKey = PublicKey.FromHexString(publicKey), + VestingSchedule = bid.VestingSchedule, }; bids.Add(bid); diff --git a/Casper.Network.SDK/Converters/HexBytesConverter.cs b/Casper.Network.SDK/Converters/HexBytesConverter.cs index b9dded7..94f2e67 100644 --- a/Casper.Network.SDK/Converters/HexBytesConverter.cs +++ b/Casper.Network.SDK/Converters/HexBytesConverter.cs @@ -19,26 +19,4 @@ public override void Write( JsonSerializerOptions options) => writer.WriteStringValue(Org.BouncyCastle.Utilities.Encoders.Hex.ToHexString(bytes)); } - - public class HexBytesWithChecksumConverter : JsonConverter - { - public override byte[] Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - var hex = reader.GetString(); - var bytes = CEP57Checksum.Decode(hex, out var checksumResult); - if (checksumResult == CEP57Checksum.InvalidChecksum) - throw new JsonException("Wrong checksum in hexadecimal string."); - - return bytes; - } - - public override void Write( - Utf8JsonWriter writer, - byte[] bytes, - JsonSerializerOptions options) => - writer.WriteStringValue(CEP57Checksum.Encode(bytes)); - } } \ No newline at end of file diff --git a/Casper.Network.SDK/ICasperClient.cs b/Casper.Network.SDK/ICasperClient.cs index 396e548..90f81cf 100644 --- a/Casper.Network.SDK/ICasperClient.cs +++ b/Casper.Network.SDK/ICasperClient.cs @@ -28,6 +28,18 @@ public interface ICasperClient Task> GetAccountInfo(string publicKey, int blockHeight); + Task> GetAccountInfo(AccountHashKey accountHash, string blockHash = null); + + Task> GetAccountInfo(AccountHashKey accountHash, int blockHeight); + + Task> GetEntity(IEntityIdentifier entityIdentifier, string blockHash = null); + + Task> GetEntity(IEntityIdentifier entityIdentifier, ulong blockHeight); + + Task> GetEntity(string entityAddr, string blockHash = null); + + Task> GetEntity(string entityAddr, ulong blockHeight); + Task> QueryGlobalState(string key, string stateRootHash = null, string path = null); @@ -66,6 +78,10 @@ Task> QueryBalanceDetailsWithStateRootHas Task> GetDeploy(string deployHash, CancellationToken cancellationToken = default(CancellationToken)); + Task> GetTransaction(TransactionHash transactionHash, + bool finalizedApprovals = false, + CancellationToken cancellationToken = default(CancellationToken)); + Task> GetBlock(string blockHash = null); Task> GetBlock(int blockHeight); diff --git a/Casper.Network.SDK/JsonRpc/CasperMethods.cs b/Casper.Network.SDK/JsonRpc/CasperMethods.cs index a29bda8..dee8ca8 100644 --- a/Casper.Network.SDK/JsonRpc/CasperMethods.cs +++ b/Casper.Network.SDK/JsonRpc/CasperMethods.cs @@ -130,6 +130,35 @@ public GetAccountInfo(string publicKey, int height) : base("state_get_account_in } } + public class GetEntity : RpcMethod + { + /// + /// Returns an AddressableEntity from the network for a Block from the network + /// + /// A PublicKey, an AccoountHashKey, or an AddressableEntityKey + /// A a block identifier by hash or key. Null for the latest block + public GetEntity(IEntityIdentifier entityIdentifier, IBlockIdentifier blockIdentifier = null) : base("state_get_entity") + { + this.Parameters = new Dictionary + { + { "entity_identifier", entityIdentifier.GetEntityIdentifier() } + }; + + if(blockIdentifier != null) + this.Parameters.Add("block_identifier", blockIdentifier.GetBlockIdentifier()); + } + + /// + /// Returns an AddressableEntity from the network for a Block from the network + /// + /// A string with an addressable entity key. + /// A a block identifier by hash or key. Null for the latest block + public GetEntity(string addressableEntity, IBlockIdentifier blockIdentifier = null) + : this(new AddressableEntityKey(addressableEntity), blockIdentifier) + { + } + } + public class GetItem : RpcMethod { /// @@ -258,6 +287,62 @@ public GetDeploy(string deployHash, bool finalizedApprovals = false) : base("inf this.Parameters.Add("finalized_approvals", true); } } + + + public class PutTransaction : RpcMethod + { + /// + /// Sends a Transaction to the network for its execution. + /// + /// The deploy object. + public PutTransaction(TransactionV1 transaction) : base("account_put_transaction") + { + this.Parameters = new Dictionary + { + { + "transaction", new Dictionary + { + { "Version1", transaction}, + } + } + }; + } + } + public class GetTransaction : RpcMethod + { + public GetTransaction(string deployHash, bool finalizedApprovals = false) : base("info_get_transaction") + { + this.Parameters = new Dictionary + { + { + "transaction_hash", new Dictionary + { + { "Deploy", deployHash } + } + }, + }; + if (finalizedApprovals) + this.Parameters.Add("finalized_approvals", true); + } + + public GetTransaction(TransactionHash transactionHash, bool finalizedApprovals = false) : base("info_get_transaction") + { + var hashDict = new Dictionary(); + if(transactionHash.Deploy != null) + hashDict.Add("Deploy", transactionHash.Deploy); + if(transactionHash.Version1 != null) + hashDict.Add("Version1", transactionHash.Version1); + + this.Parameters = new Dictionary + { + { + "transaction_hash", hashDict + }, + }; + if (finalizedApprovals) + this.Parameters.Add("finalized_approvals", true); + } + } public class GetBlock : RpcMethod { diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs index bf2fa2a..9622793 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetDeployResult.cs @@ -1,14 +1,13 @@ +using System; using System.Collections.Generic; +using System.Text.Json; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; using Casper.Network.SDK.Types; namespace Casper.Network.SDK.JsonRpc.ResultTypes { - /// - /// Result for "info_get_deploy" RPC response. - /// - public class GetDeployResult : RpcResult + public class GetDeployResultCompat : RpcResult { /// /// The deploy. @@ -16,23 +15,89 @@ public class GetDeployResult : RpcResult [JsonPropertyName("deploy")] public Deploy Deploy { get; init; } + /// + /// Execution info, if available. + /// + [JsonPropertyName("execution_info")] + public ExecutionInfo ExecutionInfo { get; init; } + /// /// The map of block hash to execution result. /// [JsonPropertyName("execution_results")] - [JsonConverter(typeof(GenericListConverter))] - public List ExecutionResults { get; init; } - + [JsonConverter(typeof(ExecutionResultV1.ExecutionResultV1Converter))] + public ExecutionResultV1 ExecutionResult { get; init; } + } + + /// + /// Result for "info_get_deploy" RPC response. + /// + [JsonConverter(typeof(GetDeployResultConverter))] + public class GetDeployResult : RpcResult + { /// - /// The hash of this deploy's block. + /// The deploy. /// - [JsonPropertyName("block_hash")] - public string BlockHash { get; init; } - + [JsonPropertyName("deploy")] + public Deploy Deploy { get; init; } + /// - /// The height of this deploy's block. + /// Execution info, if available. /// - [JsonPropertyName("block_height")] - public ulong BlockHeight { get; init; } + [JsonPropertyName("execution_info")] + public ExecutionInfo ExecutionInfo { get; init; } + + public class GetDeployResultConverter : JsonConverter + { + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert == typeof(GetDeployResult); + } + + public override GetDeployResult Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + var resultCompat = JsonSerializer.Deserialize(ref reader, options); + + if (resultCompat.ExecutionResult != null) + { + return new GetDeployResult() + { + ApiVersion = resultCompat.ApiVersion, + Deploy = resultCompat.Deploy, + ExecutionInfo = new ExecutionInfo + { + BlockHash = resultCompat.ExecutionResult.BlockHash, + BlockHeight = 0, + ExecutionResult = (ExecutionResult)resultCompat.ExecutionResult, + }, + }; + } + + return new GetDeployResult() + { + ApiVersion = resultCompat.ApiVersion, + Deploy = resultCompat.Deploy, + ExecutionInfo = resultCompat.ExecutionInfo, + }; + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + GetDeployResult block, + JsonSerializerOptions options) + { + throw new JsonException($"not yet implemented"); + } + } } -} +} \ No newline at end of file diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs new file mode 100644 index 0000000..14ed152 --- /dev/null +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetEntityResult.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Types; + +namespace Casper.Network.SDK.JsonRpc.ResultTypes +{ + /// + /// Result for \"state_get_entity\" RPC response. + /// + [JsonConverter(typeof(GetEntityResultConverter))] + public class GetEntityResult : RpcResult + { + /// + /// An addressable entity. + /// + public AddressableEntity Entity { get; init; } + + /// + /// Array of named keys present in the entity. + /// + public List NamedKeys { get; init; } + + /// + /// Array of entry points defined in the entity + /// + public List EntryPoints { get; init; } + + /// + /// A legacy account. + /// + public Account LegacyAccount { get; init; } + + /// + /// The merkle proof. + /// + public string MerkleProof { get; init; } + + public class GetEntityResultConverter : JsonConverter + { + public override GetEntityResult Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + string api_version = null; + AddressableEntity entity = null; + var namedKeys = new List(); + var entryPoints = new List(); + Account legacyAccount = null; + string merkle_proof = null; + uint skippedEntityWrapperCount = 0; + reader.Read(); + + while (reader.TokenType == JsonTokenType.PropertyName) + { + var property = reader.GetString(); + reader.Read(); + switch (property) + { + case "api_version": + api_version = reader.GetString(); + break; + case "entity": + // parse only if we already read AddressableEntity property. Otherwise, this is a wrapper for an AnddressableEntity or a LegacyAccount + if (++skippedEntityWrapperCount >= 2) + entity = JsonSerializer.Deserialize(ref reader, options); + break; + case "AddressableEntity": + // continue, this is a wrapper for entity (the actual AddressableEntity), named_keys and entry_points. + ++skippedEntityWrapperCount; + break; + case "named_keys": + namedKeys = JsonSerializer.Deserialize>(ref reader, options); + break; + case "entry_points": + entryPoints = JsonSerializer.Deserialize>(ref reader, options); + break; + case "LegacyAccount": + if (reader.TokenType != JsonTokenType.Null) + legacyAccount = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + break; + case "merkle_proof": + merkle_proof = reader.GetString(); + break; + } + + reader.Read(); + + while (reader.TokenType == JsonTokenType.EndObject && --skippedEntityWrapperCount > 0) + reader.Read(); // skip entity wrapper EndObject token and continue parsing GetEntityResult properties + } + + reader.Read(); // skip outer type EndObject token + + if(entity == null && legacyAccount == null) + throw new JsonException($"Could not deserialize GetEntityResult."); + + return new GetEntityResult() + { + ApiVersion = api_version, + Entity = entity, + NamedKeys = namedKeys, + EntryPoints = entryPoints, + LegacyAccount = legacyAccount, + MerkleProof = merkle_proof + }; + } + + public override void Write( + Utf8JsonWriter writer, + GetEntityResult value, + JsonSerializerOptions options) + { + throw new NotImplementedException("Write method for GetEntityResult not yet implemented"); + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetTransactionResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetTransactionResult.cs new file mode 100644 index 0000000..a2c493a --- /dev/null +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetTransactionResult.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Serialization; +using Casper.Network.SDK.Types; + +namespace Casper.Network.SDK.JsonRpc.ResultTypes +{ + /// + /// Result for "info_get_deploy" RPC response. + /// + public class GetTransactionResult : RpcResult + { + /// + /// The deploy. + /// + [JsonPropertyName("transaction")] + public Transaction Transaction { get; init; } + + /// + /// Execution info, if available. + /// + [JsonPropertyName("execution_info")] + public ExecutionInfo ExecutionInfo { get; init; } + } +} diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/PutTransactionResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/PutTransactionResult.cs new file mode 100644 index 0000000..21de098 --- /dev/null +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/PutTransactionResult.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; +using Casper.Network.SDK.Types; + +namespace Casper.Network.SDK.JsonRpc.ResultTypes +{ + /// + /// Result for "account_put_transaction" RPC response. + /// + public class PutTransactionResult + { + /// + /// Hex-encoded transaction hash. + /// + [JsonPropertyName("transaction_hash")] + public TransactionHash TransactionHash { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/SpeculativeExecutionResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/SpeculativeExecutionResult.cs index b0144ca..75efd7b 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/SpeculativeExecutionResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/SpeculativeExecutionResult.cs @@ -18,7 +18,7 @@ public class SpeculativeExecutionResult : RpcResult /// The result of executing the Deploy. /// [JsonPropertyName("execution_result")] - [JsonConverter(typeof(ExecutionResult.ExecutionResultConverter))] - public ExecutionResult ExecutionResult { get; init; } + [JsonConverter(typeof(ExecutionResultV1.ExecutionResultV1Converter))] + public ExecutionResultV1 ExecutionResult { get; init; } } } diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index 7558f73..b79aa53 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -3,6 +3,7 @@ using System.IO; using System.Net.Http; using System.Text; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Casper.Network.SDK.JsonRpc; @@ -177,6 +178,50 @@ public async Task> GetAccountInfo(string publi return await SendRpcRequestAsync(method); } + /// + /// Returns an AddressableEntity or a legacy Accountfrom the network for a Block from the network + /// + /// A PublicKey, an AccoountHashKey, or an AddressableEntityKey + /// A block hash for which the information of the entity is queried. Null for most recent information. + public async Task> GetEntity(IEntityIdentifier entityIdentifier, string blockHash = null) + { + var method = new GetEntity(entityIdentifier, blockHash != null ? new BlockIdentifier(blockHash) : null); + return await SendRpcRequestAsync(method); + } + + /// + /// Returns an AddressableEntity or a legacy Accountfrom the network for a Block from the network + /// + /// A PublicKey, an AccoountHashKey, or an AddressableEntityKey + /// A block height for which the information of the entity is queried.. + public async Task> GetEntity(IEntityIdentifier entityIdentifier, ulong blockHeight) + { + var method = new GetEntity(entityIdentifier, new BlockIdentifier(blockHeight)); + return await SendRpcRequestAsync(method); + } + + /// + /// Returns an AddressableEntity or a legacy Accountfrom the network for a Block from the network + /// + /// The entity address to get information of. + /// A block hash for which the information of the entity is queried. Null for most recent information. + public async Task> GetEntity(string entityAddr, string blockHash = null) + { + var method = new GetEntity(entityAddr, blockHash != null ? new BlockIdentifier(blockHash) : null); + return await SendRpcRequestAsync(method); + } + + /// + /// Returns an AddressableEntity or a legacy Accountfrom the network for a Block from the network + /// + /// The entity address to get information of. + /// A block height for which the information of the entity is queried.. + public async Task> GetEntity(string entityAddr, ulong blockHeight) + { + var method = new GetEntity(entityAddr, new BlockIdentifier(blockHeight)); + return await SendRpcRequestAsync(method); + } + /// /// Request a stored value from the network. This RPC is deprecated, use `QueryGlobalState` instead. /// @@ -394,14 +439,89 @@ public async Task> GetDeploy(string deployHash, while (!cancellationToken.IsCancellationRequested) { var response = await SendRpcRequestAsync(method); + if (!cancellationToken.CanBeCanceled) + return response; + + // Casper >= v2.0.0 processed deploy contains execution_info with data + if(response.Result.TryGetProperty("execution_info", out var executionInfo) && + executionInfo.ValueKind != JsonValueKind.Null) + return response; + + // Casper < v2.0.0 processed deploy contains execution_results with data + if(response.Result.TryGetProperty("execution_results", out var executionResults) && + executionResults.ValueKind == JsonValueKind.Array && + executionResults.GetArrayLength() > 0) + return response; + + await Task.Delay(4000); + } + + throw new TaskCanceledException("GetDeploy operation canceled"); + } + + /// + /// Send a Transaction to the network for its execution. + /// + /// The transaction object. + /// Throws an exception if the transaction is not signed. + public async Task> PutTransaction(TransactionV1 transaction) + { + if (transaction.Approvals.Count == 0) + throw new Exception("Sign the transaction before sending it to the network."); + + var method = new PutTransaction(transaction); + return await SendRpcRequestAsync(method); + } + + /// + /// Request a Transaction object from the network by the transaction (or deploy) hash. + /// When a cancellation token is included this method waits until the transaction is + /// executed, i.e. until the transaction contains the execution result information. + /// + /// An v1 transaction hash or a deploy hash + /// Whether to return the transaction with the finalized approvals + /// substituted. If `false` or omitted, returns the transaction with the approvals that were originally + /// received by the node. + /// A CancellationToken. Do not include this parameter to return + /// with the first transaction object returned by the network, even it's not executed. + /// The token has cancelled the operation before the deploy has been executed. + public async Task> GetTransaction(TransactionHash transactionHash, + bool finalizedApprovals = false, + CancellationToken cancellationToken = default(CancellationToken)) + { + var method = new GetTransaction(transactionHash, finalizedApprovals); + + while (!cancellationToken.IsCancellationRequested) + { + var response = await SendRpcRequestAsync(method); if (!cancellationToken.CanBeCanceled || - response.Result.GetProperty("execution_results").GetArrayLength() > 0) + response.Result.GetProperty("execution_info").ValueKind != JsonValueKind.Null) return response; - await Task.Delay(10000); + await Task.Delay(4000); } throw new TaskCanceledException("GetDeploy operation canceled"); } + + /// + /// Request a Transaction object from the network by the transaction hash. + /// When a cancellation token is included this method waits until the transaction is + /// executed, i.e. until the transaction contains the execution result information. + /// + /// A v1 transaction hash + /// Whether to return the transaction with the finalized approvals + /// substituted. If `false` or omitted, returns the transaction with the approvals that were originally + /// received by the node. + /// A CancellationToken. Do not include this parameter to return + /// with the first transaction object returned by the network, even it's not executed. + /// The token has cancelled the operation before the deploy has been executed. + public async Task> GetTransaction(string version1Hash, + bool finalizedApprovals = false, + CancellationToken cancellationToken = default(CancellationToken)) + { + return await this.GetTransaction(new TransactionHash { Version1 = version1Hash }, finalizedApprovals, + cancellationToken); + } /// /// Retrieves a Block from the network by its hash. diff --git a/Casper.Network.SDK/SSE/DeployAccepted.cs b/Casper.Network.SDK/SSE/DeployAccepted.cs index 009ab9b..d3458f6 100644 --- a/Casper.Network.SDK/SSE/DeployAccepted.cs +++ b/Casper.Network.SDK/SSE/DeployAccepted.cs @@ -12,7 +12,7 @@ public class DeployAccepted : Deploy // This is an alias of Deploy to be used in SSE. // public DeployAccepted(string hash, DeployHeader header, ExecutableDeployItem payment, - ExecutableDeployItem session, List approvals) + ExecutableDeployItem session, List approvals) : base(hash, header, payment, session, approvals) { } diff --git a/Casper.Network.SDK/SSE/DeployProcessed.cs b/Casper.Network.SDK/SSE/DeployProcessed.cs index 5e7185f..904b8b3 100644 --- a/Casper.Network.SDK/SSE/DeployProcessed.cs +++ b/Casper.Network.SDK/SSE/DeployProcessed.cs @@ -6,7 +6,7 @@ namespace Casper.Network.SDK.SSE { /// - /// A Deploy that has been executed, committed and forms part of a Block.. + /// A Deploy that has been executed, committed and forms part of a Block.. /// public class DeployProcessed { @@ -52,7 +52,7 @@ public class DeployProcessed /// The result of executing a this Deploy. /// [JsonPropertyName("execution_result")] - [JsonConverter(typeof(ExecutionResult.ExecutionResultConverter))] - public ExecutionResult ExecutionResult { get; init; } + [JsonConverter(typeof(ExecutionResultV1.ExecutionResultV1Converter))] + public ExecutionResultV1 ExecutionResult { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/SSE/FinalitySignature.cs b/Casper.Network.SDK/SSE/FinalitySignature.cs index 4748f2c..1d944c5 100644 --- a/Casper.Network.SDK/SSE/FinalitySignature.cs +++ b/Casper.Network.SDK/SSE/FinalitySignature.cs @@ -1,12 +1,14 @@ +using System; +using System.Text.Json; using System.Text.Json.Serialization; using Casper.Network.SDK.Types; namespace Casper.Network.SDK.SSE { /// - /// Server-Side event sent after each block finalization + /// A validator's signature of a block, confirming it is finalized. Produced in Casper v1.x /// - public class FinalitySignature + public class FinalitySignatureV1 { /// /// The block hash @@ -34,4 +36,199 @@ public class FinalitySignature [JsonConverter(typeof(Signature.SignatureConverter))] public Signature Signature { get; init; } } -} \ No newline at end of file + + + internal class FinalitySignatureV2 : FinalitySignatureV1 + { + /// + /// The block height + /// + [JsonPropertyName("block_height")] + public ulong BlockHeight { get; init; } + + /// + /// The hash of the chain name of the associated block. + /// + [JsonPropertyName("chain_name_hash")] + public string ChainNameHash { get; init; } + } + + internal class FinalitySignatureCompat + { + [JsonPropertyName("V1")] + public FinalitySignatureV1 Version1 { get; init; } + + [JsonPropertyName("V2")] + public FinalitySignatureV2 Version2 { get; init; } + + [JsonPropertyName("block_hash")] + public string BlockHash { get; init; } + + [JsonPropertyName("era_id")] + public ulong EraId { get; init; } + + [JsonPropertyName("public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey PublicKey { get; init; } + + [JsonPropertyName("signature")] + [JsonConverter(typeof(Signature.SignatureConverter))] + public Signature Signature { get; init; } + } + + /// + /// A validator's signature of a block, confirming it is finalized. + /// + [JsonConverter(typeof(FinalitySignatureConverter))] + public class FinalitySignature + { + protected int _version; + + /// + /// Returns the version of the finality signature. + /// + public int Version + { + get { return _version; } + } + + protected FinalitySignatureV1 _finalitySignatureV1; + + public static explicit operator FinalitySignatureV1(FinalitySignature finalitySignature) + { + if(finalitySignature._version == 1) + return finalitySignature._finalitySignatureV1; + + throw new InvalidCastException("Version2 FinalitySignature cannot be converted to Version1"); + } + + public static explicit operator FinalitySignature(FinalitySignatureV1 finalitySignature) + { + return new FinalitySignature + { + _version = 1, + _finalitySignatureV1 = finalitySignature, + BlockHash = finalitySignature.BlockHash, + BlockHeight = 0, + EraId = finalitySignature.EraId, + PublicKey= finalitySignature.PublicKey, + Signature = finalitySignature.Signature, + ChainNameHash = null, + }; + } + + /// + /// The block hash + /// + [JsonPropertyName("block_hash")] + public string BlockHash { get; init; } + + /// + /// The block height + /// + [JsonPropertyName("block_height")] + public ulong BlockHeight { get; init; } + + /// + /// The hash of the chain name of the associated block. + /// + [JsonPropertyName("chain_name_hash")] + public string ChainNameHash { get; init; } + + /// + /// The block era id. + /// + [JsonPropertyName("era_id")] + public ulong EraId { get; init; } + + /// + /// Validator public key + /// + [JsonPropertyName("public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey PublicKey { get; init; } + + /// + /// Validator signature + /// + [JsonPropertyName("signature")] + [JsonConverter(typeof(Signature.SignatureConverter))] + public Signature Signature { get; init; } + + /// + /// Json converter class to serialize/deserialize a FinalitySignature to/from Json + /// + public class FinalitySignatureConverter : JsonConverter + { + public override FinalitySignature Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + var fsCompat = JsonSerializer.Deserialize(ref reader, options); + if (fsCompat.BlockHash != null) + { + var v1 = new FinalitySignatureV1() + { + BlockHash = fsCompat.BlockHash, + EraId = fsCompat.EraId, + PublicKey = fsCompat.PublicKey, + Signature = fsCompat.Signature, + + }; + return (FinalitySignature)v1; + } + if (fsCompat.Version1 != null) + { + return (FinalitySignature)fsCompat.Version1; + } + if (fsCompat.Version2 != null) + { + return new FinalitySignature + { + _version = 2, + BlockHash = fsCompat.Version2.BlockHash, + BlockHeight = fsCompat.Version2.BlockHeight, + EraId = fsCompat.Version2.EraId, + PublicKey = fsCompat.Version2.PublicKey, + Signature = fsCompat.Version2.Signature, + ChainNameHash = fsCompat.Version2.ChainNameHash, + }; + } + + throw new JsonException("Cannot deserialize FinalitySignature"); + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + FinalitySignature finalitySignature, + JsonSerializerOptions options) + { + switch (finalitySignature.Version) + { + case 1: + writer.WritePropertyName("V1"); + writer.WriteStartObject(); + JsonSerializer.Serialize((FinalitySignatureV1)finalitySignature, options); + writer.WriteEndObject(); + break; + case 2: + writer.WritePropertyName("V2"); + writer.WriteStartObject(); + JsonSerializer.Serialize(finalitySignature, options); + writer.WriteEndObject(); + break; + default: + throw new JsonException($"Unexpected finality signature version {finalitySignature.Version}"); + } + } + } + } +} diff --git a/Casper.Network.SDK/SSE/ISSEClient.cs b/Casper.Network.SDK/SSE/ISSEClient.cs index 1bd562d..53fc7fa 100644 --- a/Casper.Network.SDK/SSE/ISSEClient.cs +++ b/Casper.Network.SDK/SSE/ISSEClient.cs @@ -9,10 +9,14 @@ void AddEventCallback(EventType eventType, string name, EventCallback cb, bool RemoveEventCallback(EventType eventType, string name); + int NodeVersion { get; set; } + void StartListening(); Task StopListening(); + bool IsRunning(); + void Wait(); } } diff --git a/Casper.Network.SDK/SSE/SSEvent.cs b/Casper.Network.SDK/SSE/SSEvent.cs index 0e8cbab..2464cc0 100644 --- a/Casper.Network.SDK/SSE/SSEvent.cs +++ b/Casper.Network.SDK/SSE/SSEvent.cs @@ -56,6 +56,15 @@ public T Parse() if (typeof(T) == typeof(Step)) json = this.Result.GetProperty("Step").GetRawText(); + if (typeof(T) == typeof(TransactionAccepted)) + json = this.Result.GetProperty("TransactionAccepted").GetRawText(); + + if (typeof(T) == typeof(TransactionProcessed)) + json = this.Result.GetProperty("TransactionProcessed").GetRawText(); + + if (typeof(T) == typeof(TransactionExpired)) + json = this.Result.GetProperty("TransactionExpired").GetRawText(); + if (json is null) throw new Exception($"Type '{typeof(T)} not compatible with SSE event returned object."); diff --git a/Casper.Network.SDK/SSE/ServerEventsClient.cs b/Casper.Network.SDK/SSE/ServerEventsClient.cs index f3af59c..0667f64 100644 --- a/Casper.Network.SDK/SSE/ServerEventsClient.cs +++ b/Casper.Network.SDK/SSE/ServerEventsClient.cs @@ -24,7 +24,7 @@ public enum ChannelType /// /// Channel to subscribe to only `DeployAccepted` events. /// - Sigs + Sigs, }; /// @@ -69,7 +69,19 @@ public enum EventType /// /// Add `DeployExpired` to an event callback to catch events due to an expired Deploy. /// - DeployExpired + DeployExpired, + /// + /// Add `TransactionAccepted` to an event callback to catch events due to a new Transaction accepted by the network. + /// + TransactionAccepted, + /// + /// Add `TransactionProcessed` to an event callback to catch events due to a new Transaction processed by the network. + /// + TransactionProcessed, + /// + /// Add `TransactionExpired` to an event callback to catch events due to an expired Transaction. + /// + TransactionExpired, } internal struct EventData @@ -103,6 +115,7 @@ public class ServerEventsClient : ISSEClient protected string _host; protected int _port; + protected int _nodeVersion = 1; public ServerEventsClient() { @@ -116,7 +129,10 @@ public ServerEventsClient() {EventType.Fault, ChannelType.Main}, {EventType.Step, ChannelType.Main}, {EventType.FinalitySignature, ChannelType.Sigs}, - {EventType.DeployExpired, ChannelType.Main} + {EventType.DeployExpired, ChannelType.Main}, + {EventType.TransactionAccepted, ChannelType.Main}, + {EventType.TransactionProcessed, ChannelType.Main}, + {EventType.TransactionExpired, ChannelType.Main}, }; _runningTasks = new Dictionary>(); } @@ -126,12 +142,19 @@ public ServerEventsClient() /// /// IP or domain name of the node. /// Event stream port. - public ServerEventsClient(string host, int port) : this() + public ServerEventsClient(string host, int port, int nodeVersion = 2) : this() { _host = host; _port = port; + _nodeVersion = nodeVersion; } + public int NodeVersion + { + get { return _nodeVersion; } + set { _nodeVersion = value; } + } + /// /// Adds an event callback method that is called for each subscribed event emitted by the node. /// @@ -233,6 +256,8 @@ public async Task StopListening() } await Task.WhenAll(tasks); + + _runningTasks.Clear(); } /// @@ -250,6 +275,11 @@ public void Wait() Task.WhenAll(tasks).Wait(); } + public bool IsRunning() + { + return _runningTasks.Count > 0; + } + /// /// Returns an instance of an HttpClient. Derived classes can override this method to get /// the client object from an HttpClientFactory, for example. @@ -276,8 +306,9 @@ private Task ListenChannelAsync(ChannelType channelType, int? startFrom, Cancell try { var uriBuilder = new UriBuilder(new Uri(client.BaseAddress + - $"events/{channelType.ToString().ToLowerInvariant()}")); - + $"events" + + (_nodeVersion == 1 ? $"/{channelType.ToString().ToLowerInvariant()}" : ""))); + if (startFrom != null && startFrom != int.MaxValue) uriBuilder.Query = $"start_from={startFrom}"; else diff --git a/Casper.Network.SDK/SSE/Step.cs b/Casper.Network.SDK/SSE/Step.cs index 0f39c31..8fea834 100644 --- a/Casper.Network.SDK/SSE/Step.cs +++ b/Casper.Network.SDK/SSE/Step.cs @@ -1,4 +1,6 @@ +using System.Collections.Generic; using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; namespace Casper.Network.SDK.Types { @@ -16,7 +18,8 @@ public class Step /// /// The effect of executing the deploy. /// - [JsonPropertyName("execution_effect")] - public ExecutionEffect Effect { get; init; } + [JsonPropertyName("execution_effects")] + [JsonConverter(typeof(GenericListConverter))] + public List Effects { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/SSE/TransactionAccepted.cs b/Casper.Network.SDK/SSE/TransactionAccepted.cs new file mode 100644 index 0000000..c3d29b3 --- /dev/null +++ b/Casper.Network.SDK/SSE/TransactionAccepted.cs @@ -0,0 +1,9 @@ +using System.Text.Json.Serialization; +using Casper.Network.SDK.Types; + +namespace Casper.Network.SDK.SSE +{ + public class TransactionAccepted : Transaction + { + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/SSE/TransactionExpired.cs b/Casper.Network.SDK/SSE/TransactionExpired.cs new file mode 100644 index 0000000..992988d --- /dev/null +++ b/Casper.Network.SDK/SSE/TransactionExpired.cs @@ -0,0 +1,14 @@ +using System.Text.Json.Serialization; +using Casper.Network.SDK.Types; + +namespace Casper.Network.SDK.SSE +{ + public class TransactionExpired + { + /// + /// Versioned transaction hash. + /// + [JsonPropertyName("transaction_hash")] + public TransactionHash TransactionHash { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/SSE/TransactionProcessed.cs b/Casper.Network.SDK/SSE/TransactionProcessed.cs new file mode 100644 index 0000000..0d1df2b --- /dev/null +++ b/Casper.Network.SDK/SSE/TransactionProcessed.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; +using Casper.Network.SDK.Types; + +namespace Casper.Network.SDK.SSE +{ + public class TransactionProcessed + { + /// + /// Versioned transaction hash. + /// + [JsonPropertyName("transaction_hash")] + public TransactionHash TransactionHash { get; init; } + + /// + /// The address of the initiator of a transaction. + /// + [JsonPropertyName("initiator_addr")] + public InitiatorAddr InitiatorAddr { get; set; } + + /// + /// The timestamp in which the Transaction was built. + /// + [JsonPropertyName("timestamp")] + public string Timestamp { get; init; } + + /// + /// The time-to-live of the Transaction. + /// + [JsonPropertyName("ttl")] + [JsonConverter(typeof(HumanizeTTLConverter))] + public ulong Ttl { get; set; } + + /// + /// The hash of the block containing this Transaction. + /// + [JsonPropertyName("block_hash")] + public string BlockHash { get; init; } + + /// + /// Versioned execution result. + /// + [JsonPropertyName("execution_result")] + [JsonConverter(typeof(ExecutionResult.ExecutionResultConverter))] + public ExecutionResult ExecutionResult { get; init; } + + /// + /// List of messages emitted in the transaction execution + /// + [JsonPropertyName("messages")] + public List Messages { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Account.cs b/Casper.Network.SDK/Types/Account.cs index 461499e..8ee8c4c 100644 --- a/Casper.Network.SDK/Types/Account.cs +++ b/Casper.Network.SDK/Types/Account.cs @@ -19,6 +19,12 @@ public class ActionThresholds /// [JsonPropertyName("key_management")] public uint KeyManagement { get; init; } + + /// + /// Threshold for upgrading contracts. + /// + [JsonPropertyName("upgrade_management")] + public uint UpgradeManagement { get; init; } } /// diff --git a/Casper.Network.SDK/Types/AddressableEntity.cs b/Casper.Network.SDK/Types/AddressableEntity.cs new file mode 100644 index 0000000..9810f6f --- /dev/null +++ b/Casper.Network.SDK/Types/AddressableEntity.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + public enum SystemEntityType + { + /// + /// Mint contract. + /// + Mint, + + /// + /// Handle Payment contract. + /// + HandlePayment, + + /// + /// Standard Payment contract. + /// + StandardPayment, + + /// + /// Auction contract. + /// + Auction, + } + + + [JsonConverter(typeof(EntityKindConverter))] + public class EntityKind + { + /// + /// Package associated with a native contract implementation. + /// + [JsonConverter(typeof(JsonStringEnumConverter))] + public SystemEntityType? System { get; init; } + + /// + /// Package associated with an Account hash. + /// + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public AccountHashKey Account { get; init; } + + /// + /// Packages associated with Wasm stored on chain. + /// + public TransactionRuntime? SmartContract { get; init; } + + /// + /// Json converter class to serialize/deserialize a Block to/from Json + /// + public class EntityKindConverter : JsonConverter + { + public override EntityKind Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.StartObject) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.PropertyName) + { + var property = reader.GetString(); + reader.Read(); + EntityKind entity = null; + switch (property) + { + case "Account": + entity = new EntityKind() + { + Account = new AccountHashKey(reader.GetString()), + }; + break; + case "SmartContract": + entity = new EntityKind() + { + SmartContract = EnumCompat.Parse(reader.GetString()), + }; + break; + case "System": + entity = new EntityKind() + { + System = EnumCompat.Parse(reader.GetString()), + }; + break; + } + + reader.Read(); + if (entity != null) + return entity; + } + } + + throw new JsonException("Cannot deserialize EntityKind"); + } + + public override void Write( + Utf8JsonWriter writer, + EntityKind blockHeader, + JsonSerializerOptions options) + { + throw new NotImplementedException("Write method for EntityKind not yet implemented"); + } + } + } + + /// + /// A message topic. + /// + public class MessageTopic + { + /// + /// The message topic name. + /// + [JsonPropertyName("topic_name")] + public string TopicName { get; init; } + + /// + /// The hash of the name of the message topic. + /// + [JsonPropertyName("topic_name_hash")] + public string TopicNameHash { get; init; } + } + + public class AddressableEntity + { + /// + /// Casper Platform protocol version. + /// + [JsonPropertyName("protocol_version")] + public string ProtocolVersion { get; init; } + + /// + /// The type of Package. + /// + [JsonPropertyName("entity_kind")] + public EntityKind EntityKind { get; init; } + + /// + /// The hex-encoded address of the Package. + /// + [JsonPropertyName("package_hash")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public PackageKey Package { get; init; } + + /// + /// The hash address of the contract wasm. + /// + [JsonPropertyName("byte_code_hash")] + public string ByteCodeHash { get; init; } + + [JsonPropertyName("main_purse")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public URef MainPurse { get; init; } + + /// + /// Set of public keys allowed to provide signatures on deploys for the package + /// + [JsonPropertyName("associated_keys")] + public List AssociatedKeys { get; init; } + + /// + /// Thresholds that have to be met when executing an action of a certain type. + /// + [JsonPropertyName("action_thresholds")] + public ActionThresholds ActionThresholds { get; init; } + + /// + /// Message topic list for this entity + /// + [JsonPropertyName("message_topics")] + public List MessageTopics { get; init; } + } +} + diff --git a/Casper.Network.SDK/Types/DeployApproval.cs b/Casper.Network.SDK/Types/Approval.cs similarity index 85% rename from Casper.Network.SDK/Types/DeployApproval.cs rename to Casper.Network.SDK/Types/Approval.cs index ba1699b..d2727e8 100644 --- a/Casper.Network.SDK/Types/DeployApproval.cs +++ b/Casper.Network.SDK/Types/Approval.cs @@ -1,15 +1,14 @@ using System.Text.Json.Serialization; -using Casper.Network.SDK.Converters; namespace Casper.Network.SDK.Types { /// /// Signature and Public Key of the signer. /// - public class DeployApproval + public class Approval { /// - /// Signature of a deploy. + /// Signature of a deploy or transaction. /// [JsonPropertyName("signature")] [JsonConverter(typeof(Signature.SignatureConverter))] diff --git a/Casper.Network.SDK/Types/Bid.cs b/Casper.Network.SDK/Types/Bid.cs index 09b244a..67d8b6f 100644 --- a/Casper.Network.SDK/Types/Bid.cs +++ b/Casper.Network.SDK/Types/Bid.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Numerics; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; @@ -6,7 +7,7 @@ namespace Casper.Network.SDK.Types { /// - /// An entry in a founding validator map representing a bid. + /// An entry in the validator map. /// public class Bid { @@ -24,13 +25,10 @@ public class Bid [JsonPropertyName("delegation_rate")] public uint DelegationRate { get; init; } - /// - /// The delegators. - /// [JsonPropertyName("delegators")] - [JsonConverter(typeof(GenericListConverter))] + [JsonConverter(typeof(Delegator.PublicKeyAndDelegatorListConverter))] public List Delegators { get; init; } - + /// /// `true` if validator has been "evicted" /// @@ -45,7 +43,7 @@ public class Bid public BigInteger StakedAmount { get; init; } /// - /// Validator public key + /// Validator public key. /// [JsonPropertyName("validator_public_key")] [JsonConverter(typeof(PublicKey.PublicKeyConverter))] diff --git a/Casper.Network.SDK/Types/BidKind.cs b/Casper.Network.SDK/Types/BidKind.cs new file mode 100644 index 0000000..e88fa70 --- /dev/null +++ b/Casper.Network.SDK/Types/BidKind.cs @@ -0,0 +1,97 @@ +using System.Numerics; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; + +namespace Casper.Network.SDK.Types +{ + /// + /// A bridge record pointing to a new `ValidatorBid` after the public key was changed. + /// + public class Bridge + { + + /// + /// Previous validator public key associated with the bid. + /// + [JsonPropertyName("old_validator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey OldValidator { get; init; } + + /// + /// New validator public key associated with the bid. + /// + [JsonPropertyName("new_validator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey NewValidator { get; init; } + + /// + /// Era when bridge record was created. + /// + [JsonPropertyName("era_id")] + public ulong EraId { get; init; } + + } + /// + /// Validator credit record. + /// + public class ValidatorCredit + { + /// + /// The credit amount. + /// + [JsonPropertyName("amount")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Amount { get; init; } + + /// + /// The era id the credit was created. + /// + [JsonPropertyName("era_id")] + public ulong EraId { get; init; } + + /// + /// Validator public key. + /// + [JsonPropertyName("validator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey Validator { get; init; } + } + + /// + /// Auction bid variants. + /// + public class BidKind + { + /// + /// A unified record indexed on validator data, with an embedded collection of all delegator bids assigned + /// to that validator. The Unified variant is for legacy retrograde support, new instances will not be + /// created going forward. + /// + [JsonPropertyName("Unified")] + public Bid Unified { get; init; } + + /// + /// A bid record containing only validator data. + /// + [JsonPropertyName("Validator")] + public ValidatorBid Validator { get; init; } + + /// + /// A bid record containing only delegator data. + /// + [JsonPropertyName("Delegator")] + public Delegator Delegator { get; init; } + + /// + /// A bridge record pointing to a new `ValidatorBid` after the public key was changed. + /// + [JsonPropertyName("Bridge")] + public Bridge Bridge { get; init; } + + /// + /// Credited amount. + /// + [JsonPropertyName("Credit")] + public ValidatorCredit Credit { get; init; } + } +} diff --git a/Casper.Network.SDK/Types/Block.cs b/Casper.Network.SDK/Types/Block.cs index 3b3aeb9..b84e062 100644 --- a/Casper.Network.SDK/Types/Block.cs +++ b/Casper.Network.SDK/Types/Block.cs @@ -188,21 +188,24 @@ public override BlockHeader Read( { try { - reader.Read(); - var version = reader.GetString(); - reader.Read(); - switch (version) + using (JsonDocument document = JsonDocument.ParseValue(ref reader)) { - case "Version1": - var blockHeaderv1 = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - return (BlockHeader)blockHeaderv1; - case "Version2": - var blockHeaderv2 = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - return (BlockHeader)blockHeaderv2; - default: - throw new JsonException("Expected Version1 or Version2"); + if (document.RootElement.TryGetProperty("parent_hash", out var header)) + { + var blockHeaderV1 = JsonSerializer.Deserialize(document.RootElement.GetRawText()); + return (BlockHeader)blockHeaderV1; + } + if (document.RootElement.TryGetProperty("Version1", out var headerV1)) + { + var blockHeaderV1 = JsonSerializer.Deserialize(headerV1.GetRawText()); + return (BlockHeader)blockHeaderV1; + } + if (document.RootElement.TryGetProperty("Version2", out var headerV2)) + { + var blockHeaderV2 = JsonSerializer.Deserialize(headerV1.GetRawText()); + return (BlockHeader)blockHeaderV2; + } + throw new JsonException("Cannot deserialize BlockHeader"); } } catch (Exception e) diff --git a/Casper.Network.SDK/Types/ByteCode.cs b/Casper.Network.SDK/Types/ByteCode.cs new file mode 100644 index 0000000..d7439cf --- /dev/null +++ b/Casper.Network.SDK/Types/ByteCode.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + /// + /// A container for contract's Wasm bytes. + /// + public class ByteCode + { + [JsonPropertyName("kind")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public ByteCodeKind Kind { get; init; } + + [JsonPropertyName("bytes")] + public string Bytes { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/CLValue.cs b/Casper.Network.SDK/Types/CLValue.cs index 670dd23..b8b8115 100644 --- a/Casper.Network.SDK/Types/CLValue.cs +++ b/Casper.Network.SDK/Types/CLValue.cs @@ -32,7 +32,7 @@ public class CLValue /// Byte array representation of underlying data /// [JsonPropertyName("bytes")] - [JsonConverter(typeof(HexBytesWithChecksumConverter))] + [JsonConverter(typeof(HexBytesConverter))] public byte[] Bytes { get; } /// diff --git a/Casper.Network.SDK/Types/ContractPackage.cs b/Casper.Network.SDK/Types/ContractPackage.cs index 1318dcb..0881aba 100644 --- a/Casper.Network.SDK/Types/ContractPackage.cs +++ b/Casper.Network.SDK/Types/ContractPackage.cs @@ -98,7 +98,7 @@ public class ContractPackage public List Versions { get; init; } /// - /// The current state of node reactor. + /// The current state of the contract package. /// [JsonPropertyName("lock_status")] [JsonConverter(typeof(JsonStringEnumConverter))] diff --git a/Casper.Network.SDK/Types/Delegator.cs b/Casper.Network.SDK/Types/Delegator.cs index 007c9ed..1c45be9 100644 --- a/Casper.Network.SDK/Types/Delegator.cs +++ b/Casper.Network.SDK/Types/Delegator.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Numerics; using System.Text.Json; using System.Text.Json.Serialization; @@ -6,6 +7,35 @@ namespace Casper.Network.SDK.Types { + internal class PublicKeyAndDelegator + { + [JsonPropertyName("delegator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey DelegatorPublicKey { get; init; } + + [JsonPropertyName("delegator")] + public Delegator Delegator { get; init; } + } + + internal class DelegatorV1 + { + [JsonPropertyName("bonding_purse")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public URef BondingPurse { get; init; } + + [JsonPropertyName("delegatee")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey ValidatorPublicKey { get; init; } + + [JsonPropertyName("public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey DelegatorPublicKey { get; init; } + + [JsonPropertyName("staked_amount")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger StakedAmount { get; init; } + } + /// /// A delegator associated with the given validator. /// @@ -14,103 +44,88 @@ public class Delegator /// /// The purse that was used for delegating. /// + [JsonPropertyName("bonding_purse")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public URef BondingPurse { get; init; } /// /// Public key of the validator /// - public PublicKey Delegatee { get; init; } + [JsonPropertyName("validator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey ValidatorPublicKey { get; init; } /// /// Public Key of the delegator /// - public PublicKey PublicKey { get; init; } + [JsonPropertyName("delegator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey DelegatorPublicKey { get; init; } /// /// Amount of Casper token (in motes) delegated /// + [JsonPropertyName("staked_amount")] + [JsonConverter(typeof(BigIntegerConverter))] public BigInteger StakedAmount { get; init; } + [JsonPropertyName("vesting_schedule")] public VestingSchedule VestingSchedule { get; init; } - - public class DelegatorConverter : JsonConverter, IDeserializeAsList + + public class PublicKeyAndDelegatorListConverter : JsonConverter>, IDeserializeAsList { public bool DeserializeAsList { get { return true; } } - public override Delegator Read( + public override List Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - // two possibilities called 'Delegator' and 'JsonDelegator' in the rpc schema - // An array of delegators is returned in the GetAuctionState response - // A dictionary of delegators is returned in the QueryGlobalState response for a Bid key - // This methods parses both but returns a common Delegator object in both cases - - string delegatorPublicKey = null; - if (reader.TokenType == JsonTokenType.PropertyName) - { - delegatorPublicKey = reader.GetString(); - reader.Read(); - } - - if (reader.TokenType != JsonTokenType.StartObject) - throw new JsonException("Could not deserialize Delegator. Start object token expected."); - - reader.Read(); //start object - - string public_key = null; - string amount = null; - string bonding_purse = null; - string validator_public_key = null; - VestingSchedule vesting_schedule = null; + var delegators = new List(); + + if (reader.TokenType != JsonTokenType.StartArray) + throw new JsonException("StartArray token expected to deserialize a list of delegators."); + + reader.Read(); - while (reader.TokenType == JsonTokenType.PropertyName) + while (reader.TokenType != JsonTokenType.EndArray) { - var property = reader.GetString()?.ToLowerInvariant(); - reader.Read(); - - switch (property) + try + { + using (JsonDocument document = JsonDocument.ParseValue(ref reader)) + { + if (document.RootElement.TryGetProperty("delegator_public_key", out var delegatorPublicKey)) + { + var pkAndDelegator = JsonSerializer.Deserialize(document.RootElement.GetRawText()); + delegators.Add(pkAndDelegator.Delegator); + } + else + { + var pkAndDelegator = JsonSerializer.Deserialize(document.RootElement.GetRawText()); + delegators.Add(new Delegator() + { + BondingPurse = pkAndDelegator.BondingPurse, + StakedAmount = pkAndDelegator.StakedAmount, + DelegatorPublicKey = pkAndDelegator.DelegatorPublicKey, + ValidatorPublicKey = pkAndDelegator.ValidatorPublicKey, + }); + } + } + + reader.Read(); // skip end object + } + catch (Exception e) { - case "delegator_public_key": - case "public_key": - public_key = reader.GetString(); - reader.Read(); - break; - case "bonding_purse": - bonding_purse = reader.GetString(); - reader.Read(); - break; - case "staked_amount": - amount = reader.GetString(); - reader.Read(); - break; - case "validator_public_key": - case "delegatee": - validator_public_key = reader.GetString(); - reader.Read(); - break; - case "vesting_schedule": - if(reader.TokenType != JsonTokenType.Null) - vesting_schedule = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - break; + throw new JsonException("Cannot parse list of delegators. " + e.Message); } } - return new Delegator() - { - PublicKey = PublicKey.FromHexString(public_key), - StakedAmount = BigInteger.Parse(amount), - BondingPurse = new URef(bonding_purse), - Delegatee = PublicKey.FromHexString(validator_public_key), - VestingSchedule = vesting_schedule - }; + return delegators; } public override void Write( Utf8JsonWriter writer, - Delegator value, + List value, JsonSerializerOptions options) { throw new NotImplementedException("Write method for Delegator not yet implemented"); diff --git a/Casper.Network.SDK/Types/Deploy.cs b/Casper.Network.SDK/Types/Deploy.cs index 14b2692..655ffa3 100644 --- a/Casper.Network.SDK/Types/Deploy.cs +++ b/Casper.Network.SDK/Types/Deploy.cs @@ -6,7 +6,6 @@ using System.Text.Json.Serialization; using Casper.Network.SDK.ByteSerializers; using Casper.Network.SDK.Converters; -using Casper.Network.SDK.JsonRpc; using Casper.Network.SDK.Utils; using Org.BouncyCastle.Utilities.Encoders; @@ -18,13 +17,12 @@ public class Deploy /// List of signers and signatures for this Deploy. /// [JsonPropertyName("approvals")] - public List Approvals { get; } = new List(); + public List Approvals { get; } = new List(); /// /// A hash over the header of the deploy. /// [JsonPropertyName("hash")] - [JsonConverter(typeof(CEP57Checksum.HashWithChecksumConverter))] public string Hash { get; } /// @@ -94,7 +92,7 @@ public string SerializeToJson() [JsonConstructor] public Deploy(string hash, DeployHeader header, ExecutableDeployItem payment, - ExecutableDeployItem session, List approvals) + ExecutableDeployItem session, List approvals) { this.Hash = hash; this.Header = header; @@ -115,10 +113,10 @@ public Deploy(DeployHeader header, Ttl = header.Ttl, Dependencies = header.Dependencies, GasPrice = header.GasPrice, - BodyHash = CEP57Checksum.Encode(bodyHash), + BodyHash = Hex.ToHexString(bodyHash), ChainName = header.ChainName }; - this.Hash = CEP57Checksum.Encode(ComputeHeaderHash(this.Header)); + this.Hash = Hex.ToHexString(ComputeHeaderHash(this.Header)); this.Payment = payment; this.Session = session; } @@ -130,7 +128,7 @@ public void Sign(KeyPair keyPair) { byte[] signature = keyPair.Sign(Hex.Decode(this.Hash)); - Approvals.Add(new DeployApproval() + Approvals.Add(new Approval() { Signature = Signature.FromRawBytes(signature, keyPair.PublicKey.KeyAlgorithm), Signer = keyPair.PublicKey @@ -140,7 +138,7 @@ public void Sign(KeyPair keyPair) /// /// Adds an approval to the deploy. No check is done to the approval signature. /// - public void AddApproval(DeployApproval approval) + public void AddApproval(Approval approval) { this.Approvals.Add(approval); } @@ -157,7 +155,7 @@ public bool ValidateHashes(out string message) { message = "Computed Body Hash does not match value in deploy header. " + $"Expected: '{this.Header.BodyHash}'. " + - $"Computed: '{CEP57Checksum.Encode(computedHash)}'."; + $"Computed: '{Hex.ToHexString(computedHash)}'."; return false; } @@ -166,7 +164,7 @@ public bool ValidateHashes(out string message) { message = "Computed Hash does not match value in deploy object. " + $"Expected: '{this.Hash}'. " + - $"Computed: '{CEP57Checksum.Encode(computedHash)}'."; + $"Computed: '{Hex.ToHexString(computedHash)}'."; return false; } diff --git a/Casper.Network.SDK/Types/DeployHeader.cs b/Casper.Network.SDK/Types/DeployHeader.cs index ce218e3..4e4e037 100644 --- a/Casper.Network.SDK/Types/DeployHeader.cs +++ b/Casper.Network.SDK/Types/DeployHeader.cs @@ -47,7 +47,6 @@ public class DeployHeader /// Hash of the body part of this Deploy. /// [JsonPropertyName("body_hash")] - [JsonConverter(typeof(CEP57Checksum.HashWithChecksumConverter))] public string BodyHash { get; set; } /// diff --git a/Casper.Network.SDK/Types/EntryPoint.cs b/Casper.Network.SDK/Types/EntryPoint.cs index 71be3e9..1fba02e 100644 --- a/Casper.Network.SDK/Types/EntryPoint.cs +++ b/Casper.Network.SDK/Types/EntryPoint.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text.Json; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; @@ -77,13 +78,50 @@ public override void Write( public enum EntryPointType { /// - /// Runs as session code + /// Runs using the calling entity's context. In v1.x this was used for both \"session\" code run using the + /// originating Account's context, and also for \"StoredSession\" code that ran in the caller's context. + /// While this made systemic sense due to the way the runtime context nesting works, this dual usage was + /// very confusing to most human beings. + /// In v2.x the renamed Caller variant is exclusively used for wasm run using the initiating account entity's + /// context. Previously installed 1.x stored session code should continue to work as the binary value matches + /// but we no longer allow such logic to be upgraded, nor do we allow new stored session to be installed. + /// + Caller, + /// + /// Runs using the called entity's context. + /// + Called, + /// + /// Extract a subset of bytecode and installs it as a new smart contract. Runs using the called entity's context. + /// + Factory, + /// + /// Casper v1.x only. Executes in the contract context. + /// + Contract, + /// + /// Casper v1.x only. Executes in the caller account context. /// Session, + } + + /// + /// Defines who pays for the execution of the entry point. + /// + public enum EntryPointPayment + { + /// + /// The caller must cover cost. + /// + Caller, /// - /// Runs within contract’s context + /// Will cover cost to execute self but not cost of any subsequent invoked contracts. /// - Contract + SelfOnly, + /// + /// Will cover cost to execute self and the cost of any subsequent invoked contracts. + /// + SelfOnward, } /// @@ -130,6 +168,13 @@ public class EntryPoint [JsonConverter(typeof(JsonStringEnumConverter))] public EntryPointType EntryPointType { get; init; } + /// + /// Specifies who pays for the invocation and execution of the entrypoint. + /// + [JsonPropertyName("entry_point_payment")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public EntryPointPayment EntryPointPayment { get; init; } + /// /// Name of the entry point /// @@ -143,4 +188,51 @@ public class EntryPoint [JsonConverter(typeof(CLTypeInfoConverter))] public CLTypeInfo Ret { get; init; } } + + public class EntryPointV2 + { + public EntryPointV2() + { + throw new NotImplementedException("V2CasperVm entry point not yet implemented"); + } + } + + public class VersionedEntryPoint + { + public EntryPoint V1CasperVm { get; init; } + + public EntryPointV2 V2CasperVm { get; init; } + } + + public class NamedEntryPoint + { + [JsonPropertyName("name")] + public string Name { get; init; } + + [JsonPropertyName("entry_point")] + public EntryPoint EntryPoint { get; init; } + + public class NamedEntryPointsConverter : JsonConverter> + { + public override List Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + var namedEntryPoints = JsonSerializer.Deserialize>(ref reader, options); + if (namedEntryPoints != null) + return namedEntryPoints.Select(e => e.EntryPoint).ToList(); + + throw new JsonException("Cannot deserialize Array_of_NamedEntryPoint."); + } + + public override void Write( + Utf8JsonWriter writer, + List value, + JsonSerializerOptions options) + { + throw new NotImplementedException("Write method for Array_of_NamedEntryPoint not yet implemented."); + } + } + } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/ExecutableDeployItem.cs b/Casper.Network.SDK/Types/ExecutableDeployItem.cs index 0524876..c4b71e6 100644 --- a/Casper.Network.SDK/Types/ExecutableDeployItem.cs +++ b/Casper.Network.SDK/Types/ExecutableDeployItem.cs @@ -78,7 +78,6 @@ public class StoredContractByHashDeployItem : ExecutableDeployItem /// Hash of the contract. /// [JsonPropertyName("hash")] - [JsonConverter(typeof(CEP57Checksum.HashWithChecksumConverter))] public string Hash { get; init; } /// @@ -161,7 +160,6 @@ public class StoredVersionedContractByHashDeployItem : ExecutableDeployItem /// Hash of the contract package. /// [JsonPropertyName("hash")] - [JsonConverter(typeof(CEP57Checksum.HashWithChecksumConverter))] public string Hash { get; init; } /// diff --git a/Casper.Network.SDK/Types/ExecutionEffect.cs b/Casper.Network.SDK/Types/ExecutionEffect.cs index 1bec2ff..9ebd592 100644 --- a/Casper.Network.SDK/Types/ExecutionEffect.cs +++ b/Casper.Network.SDK/Types/ExecutionEffect.cs @@ -19,7 +19,7 @@ public class ExecutionEffect /// The journal of execution transforms. /// [JsonPropertyName("transforms")] - [JsonConverter(typeof(GenericListConverter))] - public List Transforms { get; init; } + [JsonConverter(typeof(GenericListConverter))] + public List Transforms { get; init; } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/ExecutionInfo.cs b/Casper.Network.SDK/Types/ExecutionInfo.cs new file mode 100644 index 0000000..a441b1b --- /dev/null +++ b/Casper.Network.SDK/Types/ExecutionInfo.cs @@ -0,0 +1,26 @@ +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + public class ExecutionInfo + { + /// + /// The hash of the block in which the transaction/deploy was executed. + /// + [JsonPropertyName("block_hash")] + public string BlockHash { get; init; } + + /// + /// The height of the block in which the transaction/deploy was executed. + /// + [JsonPropertyName("block_height")] + public ulong BlockHeight { get; init; } + + /// + /// The map of block hash to execution result. + /// + [JsonPropertyName("execution_result")] + [JsonConverter(typeof(ExecutionResult.ExecutionResultConverter))] + public ExecutionResult ExecutionResult { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/ExecutionResult.cs b/Casper.Network.SDK/Types/ExecutionResult.cs index b11d41d..f00748c 100644 --- a/Casper.Network.SDK/Types/ExecutionResult.cs +++ b/Casper.Network.SDK/Types/ExecutionResult.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Numerics; using System.Text.Json; using System.Text.Json.Serialization; @@ -7,10 +8,184 @@ namespace Casper.Network.SDK.Types { + public class ExecutionResult + { + protected int _version; + + /// + /// Returns the version of the block. + /// + public int Version + { + get { return _version; } + } + + protected ExecutionResultV1 _executionResultV1; + + public static explicit operator ExecutionResultV1(ExecutionResult executionResult) + { + if(executionResult._version == 1) + return executionResult._executionResultV1; + + throw new InvalidCastException("Version2 execution result cannot be converted to Version1"); + } + + public static explicit operator ExecutionResult(ExecutionResultV1 executionResult) + { + var v2Transfers = executionResult.Transfers.Select(key => + { + var transform = executionResult.Effect.Transforms.FirstOrDefault(tr => tr.Key.Equals(key)); + if (transform != null && + transform.Kind == TransformKindV1.WriteTransfer && + transform.Value is TransferV1 transferV1) + { + return (Transfer)transferV1; + } + return null; + }).ToList(); + + var v2Effect = executionResult.Effect.Transforms.Select(t => (Transform)t).ToList(); + + return new ExecutionResult + { + _version = 1, + _executionResultV1 = executionResult, + ErrorMessage = executionResult.ErrorMessage, + // Transfers = executionResult.Transfers, + Cost = executionResult.Cost, + Limit = 0, + Consumed = 0, + Effect = v2Effect, + SizeEstimate = 0, + Transfers = v2Transfers, + }; + } + + /// + /// What was the maximum allowed gas limit for this transaction?. + /// + [JsonPropertyName("limit")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Limit { get; init; } + + /// + /// How much gas was consumed executing this transaction. + /// + [JsonPropertyName("consumed")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Consumed { get; init; } + + /// + /// How much was paid for this transaction. + /// + [JsonPropertyName("cost")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Cost { get; init; } + + /// + /// If there is no error message, this execution was processed successfully. If there is an error message, this + /// execution failed to fully process for the stated reason. + /// + [JsonPropertyName("error_message")] + public string ErrorMessage { get; init; } + + /// + /// A record of transfers performed while executing this transaction. + /// + [JsonPropertyName("transfers")] + public List Transfers { get; init; } + + /// + /// The size estimate of the transaction + /// + [JsonPropertyName("size_estimate")] + public UInt64 SizeEstimate { get; init; } + + /// + /// A log of all transforms produced during execution. + /// + [JsonPropertyName("effects")] + [JsonConverter(typeof(GenericListConverter))] + public List Effect { get; init; } + + /// + /// Json converter class to serialize/deserialize a ExecutionResult to/from Json + /// + public class ExecutionResultConverter : JsonConverter + { + public override ExecutionResult Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + reader.Read(); + var version = reader.GetString(); + reader.Read(); + switch (version) + { + case "Version1": + var erSerializerOptions = new JsonSerializerOptions(options); + erSerializerOptions.Converters.Add(new ExecutionResultV1.ExecutionResultV1Converter()); + + var erV1 = JsonSerializer.Deserialize(ref reader, erSerializerOptions); + reader.Read(); + return (ExecutionResult)erV1; + case "Version2": + var erv2 = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + return new ExecutionResult + { + _version = 2, + ErrorMessage = erv2.ErrorMessage, + Transfers = erv2.Transfers, + Cost = erv2.Cost, + Limit = erv2.Limit, + Consumed = erv2.Consumed, + Effect = erv2.Effect, + SizeEstimate = erv2.SizeEstimate, + }; + default: + throw new JsonException("Expected Version1 or Version2"); + } + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + ExecutionResult executionResult, + JsonSerializerOptions options) + { + switch (executionResult.Version) + { + case 1: + writer.WritePropertyName("Version1"); + writer.WriteStartObject(); + JsonSerializer.Serialize((ExecutionResultV1)executionResult, options); + writer.WriteEndObject(); + break; + case 2: + writer.WritePropertyName("Version2"); + writer.WriteStartObject(); + JsonSerializer.Serialize(executionResult, options); + writer.WriteEndObject(); + break; + default: + throw new JsonException($"Unexpected execution result version {executionResult.Version}"); + } + } + } + } + /// /// The result of executing a single deploy. /// - public class ExecutionResult + public class ExecutionResultV1 { [JsonIgnore] public bool IsSuccess { get; init; } @@ -45,25 +220,30 @@ public class ExecutionResult [JsonConverter(typeof(GenericListConverter))] public List Transfers { get; init; } - public ExecutionResult() + public ExecutionResultV1() { Transfers = new List(); } - public class ExecutionResultConverter : JsonConverter + public class ExecutionResultV1Converter : JsonConverter { - public override ExecutionResult Read( + public override ExecutionResultV1 Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + var firstTokenType = reader.TokenType; + + if(firstTokenType == JsonTokenType.StartArray) //skip start array (backward compat) + reader.Read(); + if (reader.TokenType == JsonTokenType.StartObject) reader.Read(); string blockHash = null; string result = null; bool readResult = false; - ExecutionResult executionResult = null; + ExecutionResultV1 executionResult = null; while (reader.TokenType == JsonTokenType.PropertyName) { @@ -87,7 +267,7 @@ public override ExecutionResult Read( if (reader.TokenType != JsonTokenType.StartObject) throw new JsonException($"Object expected after '{result}' during ExecutionResult deserialization"); - executionResult = JsonSerializer.Deserialize(ref reader); + executionResult = JsonSerializer.Deserialize(ref reader); reader.Read(); // end Success/Failure object @@ -99,11 +279,17 @@ public override ExecutionResult Read( break; } } + + if(firstTokenType == JsonTokenType.StartArray) + reader.Read(); // skip end object + + // if (reader.TokenType == JsonTokenType.EndArray) + // reader.Read(); //skip end array (backward compat) if (executionResult == null) throw new JsonException("Could not deserialize ExecutionResult object"); - return new ExecutionResult + return new ExecutionResultV1 { BlockHash = blockHash, IsSuccess = result.Equals("Success", StringComparison.InvariantCultureIgnoreCase), @@ -116,11 +302,61 @@ public override ExecutionResult Read( public override void Write( Utf8JsonWriter writer, - ExecutionResult item, + ExecutionResultV1 item, JsonSerializerOptions options) { throw new NotImplementedException("Serialization of ExecutionResult not implemented"); } } } -} \ No newline at end of file + + internal class ExecutionResultV2 + { + /// + /// What was the maximum allowed gas limit for this transaction?. + /// + [JsonPropertyName("limit")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Limit { get; init; } + + /// + /// How much gas was consumed executing this transaction. + /// + [JsonPropertyName("consumed")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Consumed { get; init; } + + /// + /// How much was paid for this transaction. + /// + [JsonPropertyName("cost")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Cost { get; init; } + + /// + /// If there is no error message, this execution was processed successfully. If there is an error message, this + /// execution failed to fully process for the stated reason. + /// + [JsonPropertyName("error_message")] + public string ErrorMessage { get; init; } + + /// + /// A record of transfers performed while executing this transaction. + /// + [JsonPropertyName("transfers")] + public List Transfers { get; init; } + + /// + /// The size estimate of the transaction + /// + [JsonPropertyName("size_estimate")] + public UInt64 SizeEstimate { get; init; } + + /// + /// A log of all transforms produced during execution. + /// + [JsonPropertyName("effects")] + [JsonConverter(typeof(GenericListConverter))] + public List Effect { get; init; } + } +} diff --git a/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs index 7340038..fcd4ba4 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/AddressableEntityKey.cs @@ -1,26 +1,31 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; using Casper.Network.SDK.Utils; using Org.BouncyCastle.Utilities.Encoders; namespace Casper.Network.SDK.Types { - public enum EntityKindEnum { + public enum EntityKindEnum + { /// /// Package associated with a native contract implementation. /// System = 0, + /// /// Package associated with an Account hash. /// Account = 1, + /// /// Package associated with Wasm stored on chain. /// Contract = 2, } - + public static class EntityKinEnumExtensions { public static string ToKeyPrefix(this EntityKindEnum kind) @@ -38,11 +43,11 @@ public static string ToKeyPrefix(this EntityKindEnum kind) } } } - + public class AddressableEntityKey : GlobalStateKey, IEntityIdentifier, IPurseIdentifier { public EntityKindEnum Kind { get; init; } - + private static string GetPrefix(string key) { if (key.StartsWith(EntityKindEnum.System.ToKeyPrefix())) @@ -53,8 +58,8 @@ private static string GetPrefix(string key) return EntityKindEnum.Contract.ToKeyPrefix(); throw new Exception("Unexpected key prefix in NamedKeyKey: " + key); - } + public AddressableEntityKey(string key) : base(key, GetPrefix(key)) { KeyIdentifier = KeyIdentifier.AddressableEntity; @@ -67,7 +72,7 @@ public AddressableEntityKey(string key) : base(key, GetPrefix(key)) Kind = EntityKindEnum.Contract; } - public AddressableEntityKey(byte[] key) : this( + public AddressableEntityKey(byte[] key) : this( key[0] == (byte)EntityKindEnum.System ? EntityKindEnum.System.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) : (key[0] == (byte)EntityKindEnum.Account @@ -86,15 +91,15 @@ public AddressableEntityKey(BinaryReader reader) : base(null) Kind = (EntityKindEnum)tag; Key = Kind.ToKeyPrefix() + Hex.ToHexString(addr); } - + protected override byte[] _GetRawBytesFromKey(string key) { return this.GetBytes(); } - + public override byte[] GetBytes() { - var key = Key.Substring(Key.LastIndexOf('-')+1); + var key = Key.Substring(Key.LastIndexOf('-') + 1); var rawBytes = Hex.Decode(key); var ms = new MemoryStream(); ms.WriteByte((byte)this.Kind); @@ -102,7 +107,7 @@ public override byte[] GetBytes() return ms.ToArray(); } - + /// /// Returns an EntityIdentifier object as defined in the RPC schema for an account hash key. /// @@ -110,10 +115,10 @@ public Dictionary GetEntityIdentifier() { return new Dictionary { - {"EntityAddr", Key} + { "EntityAddr", Key } }; } - + /// /// Returns a PurseIdentifier object as defined in the RPC schema for an entity address /// @@ -121,8 +126,34 @@ public Dictionary GetPurseIdentifier() { return new Dictionary { - {"main_purse_under_entity_addr", this.ToString()} + { "main_purse_under_entity_addr", this.ToString() } }; } + + public class AddressableEntityKeyConverter : JsonConverter + { + public override AddressableEntityKey Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + return new AddressableEntityKey(reader.GetString()); + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + AddressableEntityKey addressableEntity, + JsonSerializerOptions options) + { + writer.WriteStringValue(addressableEntity.Key); + } + } } -} +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey/BalanceHoldKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/BalanceHoldKey.cs index b675eff..2e28000 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/BalanceHoldKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/BalanceHoldKey.cs @@ -1,5 +1,5 @@ using System; -using Casper.Network.SDK.Utils; +using Org.BouncyCastle.Utilities.Encoders; namespace Casper.Network.SDK.Types { @@ -56,7 +56,7 @@ public BalanceHoldKey(string key) : base(key, KEYPREFIX) throw new Exception("Wrong key length for BalanceHold. Expected 41 bytes."); } - public BalanceHoldKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + public BalanceHoldKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } } diff --git a/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs index 697aaa1..e08de93 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs @@ -1,5 +1,4 @@ using System; -using Casper.Network.SDK.Utils; using Org.BouncyCastle.Utilities.Encoders; namespace Casper.Network.SDK.Types @@ -91,8 +90,8 @@ public BidAddrKey(string key) : base(key, KEYPREFIX) } } - public BidAddrKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + public BidAddrKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } } -} \ No newline at end of file +} diff --git a/Casper.Network.SDK/Types/GlobalStateKey/BlockGlobalAddrKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/BlockGlobalAddrKey.cs index 55ea4eb..35bdebf 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/BlockGlobalAddrKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/BlockGlobalAddrKey.cs @@ -1,5 +1,5 @@ using System; -using Casper.Network.SDK.Utils; +using Org.BouncyCastle.Utilities.Encoders; namespace Casper.Network.SDK.Types { @@ -60,9 +60,9 @@ public BlockGlobalAddrKey(string key) : base(key, GetPrefix(key)) public BlockGlobalAddrKey(byte[] key) : this( key[0] == (byte)BlockGlobalAddrTag.Time - ? BlockGlobalAddrTag.Time.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + ? BlockGlobalAddrTag.Time.ToKeyPrefix() + Hex.ToHexString(key.Slice(1)) : (key[0] == (byte)BlockGlobalAddrTag.MessageCount - ? BlockGlobalAddrTag.MessageCount.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + ? BlockGlobalAddrTag.MessageCount.ToKeyPrefix() + Hex.ToHexString(key.Slice(1)) : throw new Exception($"Wrong kind tag '{key[0]}' for BlockGlobalAddrKey."))) { } diff --git a/Casper.Network.SDK/Types/GlobalStateKey/ByteCodeKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/ByteCodeKey.cs index 7acc9aa..e4f772a 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/ByteCodeKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/ByteCodeKey.cs @@ -1,5 +1,5 @@ using System; -using Casper.Network.SDK.Utils; +using Org.BouncyCastle.Utilities.Encoders; namespace Casper.Network.SDK.Types { @@ -55,9 +55,9 @@ public ByteCodeKey(string key) : base(key, ByteCodeKey.GetPrefix(key)) public ByteCodeKey(byte[] key) : this( key[0] == (byte)ByteCodeKind.Empty - ? ByteCodeKind.Empty.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + ? ByteCodeKind.Empty.ToKeyPrefix() + Hex.ToHexString(key.Slice(1)) : (key[0] == (byte)ByteCodeKind.V1CasperWasm - ? ByteCodeKind.V1CasperWasm.ToKeyPrefix() + CEP57Checksum.Encode(key.Slice(1)) + ? ByteCodeKind.V1CasperWasm.ToKeyPrefix() + Hex.ToHexString(key.Slice(1)) : throw new Exception($"Wrong kind tag '{key[0]}' for ByteCodeKey."))) { } diff --git a/Casper.Network.SDK/Types/GlobalStateKey/EntryPointKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/EntryPointKey.cs new file mode 100644 index 0000000..9865f31 --- /dev/null +++ b/Casper.Network.SDK/Types/GlobalStateKey/EntryPointKey.cs @@ -0,0 +1,47 @@ +using System; +using Casper.Network.SDK.Utils; + +namespace Casper.Network.SDK.Types +{ + public class EntryPointKey : GlobalStateKey + { + public static string ENTRYPOINT_PREFIX = "entry-point-"; + public static string V1_PREFIX = "v1-"; + public static string V2_PREFIX = "v2-"; + + public AddressableEntityKey AddressableEntity { get; init; } + + public string NameHash { get; init; } + + public UInt32 Separator { get; init; } + + public EntryPointKey(string key) : base(key) + { + KeyIdentifier = KeyIdentifier.EntryPoint; + + if (!key.StartsWith(ENTRYPOINT_PREFIX)) + throw new ArgumentException($"Key not valid. It should start with '{ENTRYPOINT_PREFIX}'."); + key = key.Substring(ENTRYPOINT_PREFIX.Length); + + if (key.StartsWith(V1_PREFIX)) + { + key = key.Substring(V1_PREFIX.Length); + var parts = key.Split('-'); + if (parts.Length != 4) + throw new Exception("Key not valid. It should have an entity address and a name hash."); + + AddressableEntity = new AddressableEntityKey($"{parts[0]}-{parts[1]}-{parts[2]}"); + NameHash = parts[3]; + } + else if (key.StartsWith(V2_PREFIX)) + { + throw new Exception($"entry-point-v2 not yet supported. {key}."); + } + } + + public EntryPointKey(byte[] key) : base(null) + { + throw new Exception($"entry-point key from bytes not yet supported. {key}."); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs index f8ade9f..1655fed 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/GlobalStateKey.cs @@ -104,6 +104,10 @@ public enum KeyIdentifier /// A `Key` under which a hold on a purse balance is stored. /// BalanceHold = 0x16, + /// + /// A `Key` under which a entrypoint record is written. + /// + EntryPoint = 0x17, } /// @@ -138,12 +142,12 @@ protected GlobalStateKey(string key, string keyPrefix) if (checksumResult == CEP57Checksum.InvalidChecksum) throw new ArgumentException("Global State Key checksum mismatch."); - Key = keyPrefix + CEP57Checksum.Encode(bytes); + Key = keyPrefix + Hex.ToHexString(bytes); } public string ToHexString() { - return CEP57Checksum.Encode(RawBytes); + return Hex.ToHexString(RawBytes); } /// @@ -207,6 +211,8 @@ public static GlobalStateKey FromString(string value) return new ByteCodeKey(value); if (value.StartsWith("message-")) return new MessageKey(value); + if (value.StartsWith("entry-point-")) + return new EntryPointKey(value); throw new ArgumentException($"Key not valid. Unknown key prefix in \"{value}\"."); } @@ -218,29 +224,30 @@ public static GlobalStateKey FromBytes(byte[] bytes) { return bytes[0] switch { - 0x00 => new AccountHashKey("account-hash-" + CEP57Checksum.Encode(bytes.Slice(1))), - 0x01 => new HashKey("hash-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x00 => new AccountHashKey("account-hash-" + Hex.ToHexString(bytes.Slice(1))), + 0x01 => new HashKey("hash-" + Hex.ToHexString(bytes.Slice(1))), 0x02 => new URef(bytes.Slice(1)), - 0x03 => new TransferKey("transfer-" + CEP57Checksum.Encode(bytes.Slice(1))), - 0x04 => new DeployInfoKey("deploy-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x03 => new TransferKey("transfer-" + Hex.ToHexString(bytes.Slice(1))), + 0x04 => new DeployInfoKey("deploy-" + Hex.ToHexString(bytes.Slice(1))), 0x05 => new EraInfoKey("era-" + BitConverter.ToInt64(bytes, 1)), - 0x06 => new BalanceKey("balance-" + CEP57Checksum.Encode(bytes.Slice(1))), - 0x07 => new BidKey("bid-" + CEP57Checksum.Encode(bytes.Slice(1))), - 0x08 => new WithdrawKey("withdraw-" + CEP57Checksum.Encode(bytes.Slice(1))), - 0x09 => new DictionaryKey("dictionary-" + CEP57Checksum.Encode(bytes.Slice(1))), - 0x0a => new SystemContractRegistryKey("system-contract-registry-" + CEP57Checksum.Encode(bytes.Slice(1))), - 0x0b => new EraSummaryKey("era-summary-" + CEP57Checksum.Encode(bytes.Slice(1))), - 0x0c => new UnbondKey("unbond-" + CEP57Checksum.Encode(bytes.Slice(1))), - 0x0d => new ChainspecRegistryKey("chainspec-registry-" + CEP57Checksum.Encode(bytes.Slice(1))), - 0x0e => new ChecksumRegistryKey("checksum-registry-" + CEP57Checksum.Encode(bytes.Slice(1))), - 0x0f => new BidAddrKey("bid-addr-" + CEP57Checksum.Encode(bytes.Slice(1))), - 0x10 => new PackageKey("package-" + CEP57Checksum.Encode(bytes.Slice(1))), + 0x06 => new BalanceKey("balance-" + Hex.ToHexString(bytes.Slice(1))), + 0x07 => new BidKey("bid-" + Hex.ToHexString(bytes.Slice(1))), + 0x08 => new WithdrawKey("withdraw-" + Hex.ToHexString(bytes.Slice(1))), + 0x09 => new DictionaryKey("dictionary-" + Hex.ToHexString(bytes.Slice(1))), + 0x0a => new SystemContractRegistryKey("system-contract-registry-" + Hex.ToHexString(bytes.Slice(1))), + 0x0b => new EraSummaryKey("era-summary-" + Hex.ToHexString(bytes.Slice(1))), + 0x0c => new UnbondKey("unbond-" + Hex.ToHexString(bytes.Slice(1))), + 0x0d => new ChainspecRegistryKey("chainspec-registry-" + Hex.ToHexString(bytes.Slice(1))), + 0x0e => new ChecksumRegistryKey("checksum-registry-" + Hex.ToHexString(bytes.Slice(1))), + 0x0f => new BidAddrKey("bid-addr-" + Hex.ToHexString(bytes.Slice(1))), + 0x10 => new PackageKey("package-" + Hex.ToHexString(bytes.Slice(1))), 0x11 => new AddressableEntityKey(bytes.Slice(1)), 0x12 => new ByteCodeKey(bytes.Slice(1)), 0x13 => new MessageKey(bytes.Slice(1)), 0x14 => new NamedKeyKey(bytes.Slice(1)), 0x15 => new BlockGlobalAddrKey(bytes.Slice(1)), 0x16 => new BalanceHoldKey(bytes.Slice(1)), + 0x17 => new EntryPointKey(bytes.Slice(1)), _ => throw new ArgumentException($"Unknown key identifier '{bytes[0]}'") }; } @@ -262,6 +269,22 @@ public override string ToString() return Key; } + public override bool Equals(object obj) + { + //Check for null and compare run-time types. + if ((obj == null) || ! this.GetType().Equals(obj.GetType())) + { + return false; + } + + return Key.ToLowerInvariant().Equals(((GlobalStateKey) obj).Key.ToLowerInvariant()); + } + + public override int GetHashCode() + { + return Key.ToLowerInvariant().GetHashCode(); + } + /// /// Json converter class to serialize/deserialize an object derived from /// GlobalStateKey to/from Json @@ -293,7 +316,8 @@ public override bool CanConvert(Type typeToConvert) typeToConvert == typeof(MessageKey) || typeToConvert == typeof(NamedKeyKey) || typeToConvert == typeof(BlockGlobalAddrKey) || - typeToConvert == typeof(BalanceHoldKey); + typeToConvert == typeof(BalanceHoldKey) || + typeToConvert == typeof(EntryPointKey); } public override JsonConverter CreateConverter( @@ -344,7 +368,7 @@ public override void Write( /// Stores an account in the global state. /// Format: 32-byte length with prefix 'account-hash-'. /// - public class AccountHashKey : GlobalStateKey, IPurseIdentifier + public class AccountHashKey : GlobalStateKey, IPurseIdentifier, IEntityIdentifier { public static string KEYPREFIX = "account-hash-"; @@ -357,8 +381,8 @@ public AccountHashKey(PublicKey publicKey) : base(publicKey.GetAccountHash(), KEYPREFIX) { } - - public AccountHashKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + + public AccountHashKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } @@ -372,6 +396,17 @@ public Dictionary GetPurseIdentifier() {"main_purse_under_account_hash", this.ToString()} }; } + + /// + /// Returns an EntityIdentifier object as defined in the RPC schema for an account hash key. + /// + public Dictionary GetEntityIdentifier() + { + return new Dictionary + { + {"AccountHash", this.ToString()} + }; + } } /// @@ -387,7 +422,7 @@ public HashKey(string key) : base(key, KEYPREFIX) KeyIdentifier = KeyIdentifier.Hash; } - public HashKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + public HashKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } } @@ -405,7 +440,7 @@ public TransferKey(string key) : base(key, KEYPREFIX) KeyIdentifier = KeyIdentifier.Transfer; } - public TransferKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + public TransferKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } } @@ -423,7 +458,7 @@ public DeployInfoKey(string key) : base(key, KEYPREFIX) KeyIdentifier = KeyIdentifier.DeployInfo; } - public DeployInfoKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + public DeployInfoKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } } @@ -488,7 +523,7 @@ public BalanceKey(string key) : base(key, KEYPREFIX) KeyIdentifier = KeyIdentifier.Balance; } - public BalanceKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + public BalanceKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } } @@ -506,7 +541,7 @@ public BidKey(string key) : base(key, KEYPREFIX) KeyIdentifier = KeyIdentifier.Bid; } - public BidKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + public BidKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } } @@ -524,7 +559,7 @@ public WithdrawKey(string key) : base(key, KEYPREFIX) KeyIdentifier = KeyIdentifier.Withdraw; } - public WithdrawKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + public WithdrawKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } } @@ -542,7 +577,7 @@ public DictionaryKey(string key) : base(key, KEYPREFIX) KeyIdentifier = KeyIdentifier.Dictionary; } - public DictionaryKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + public DictionaryKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } } @@ -566,7 +601,7 @@ public SystemContractRegistryKey(string key) : base(key, KEYPREFIX) KeyIdentifier = KeyIdentifier.SystemContractRegistry; } - public SystemContractRegistryKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + public SystemContractRegistryKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } } @@ -598,7 +633,7 @@ public UnbondKey(string key) : base(key, KEYPREFIX) KeyIdentifier = KeyIdentifier.Unbond; } - public UnbondKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + public UnbondKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } } @@ -622,7 +657,7 @@ public ChainspecRegistryKey(string key) : base(key, KEYPREFIX) KeyIdentifier = KeyIdentifier.ChainspecRegistry; } - public ChainspecRegistryKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + public ChainspecRegistryKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } } @@ -645,7 +680,7 @@ public ChecksumRegistryKey(string key) : base(key, KEYPREFIX) KeyIdentifier = KeyIdentifier.ChecksumRegistry; } - public ChecksumRegistryKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + public ChecksumRegistryKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } } diff --git a/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs index 077696c..2e14080 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs @@ -7,35 +7,22 @@ namespace Casper.Network.SDK.Types { public class MessageKey : GlobalStateKey { - private const string MESSAGE_PREFIX = "message-"; + private const string KEY_PREFIX = "message-"; private const string TOPIC_PREFIX = "topic-"; - private static readonly string CONTRACT_TOPIC_KEYPREFIX = "message-topic-entity-contract-"; - private static readonly string CONTRACT_MSG_KEYPREFIX = "message-entity-contract-"; - public AddressableEntityKey AddressableEntity { get; init; } public string TopicHash { get; init; } public UInt32? Index { get; init; } - private static string GetPrefix(string key) - { - if (key.StartsWith(CONTRACT_TOPIC_KEYPREFIX)) - return CONTRACT_TOPIC_KEYPREFIX; - if (key.StartsWith(CONTRACT_MSG_KEYPREFIX)) - return CONTRACT_MSG_KEYPREFIX; - - throw new Exception("Unexpected key prefix in ByteCodeKey: " + key); - } - public MessageKey(string key) : base(key) { KeyIdentifier = KeyIdentifier.Message; - if (!key.StartsWith(MESSAGE_PREFIX)) - throw new ArgumentException($"Key not valid. It should start with '{MESSAGE_PREFIX}'."); - key = key.Substring(MESSAGE_PREFIX.Length); + if (!key.StartsWith(KEY_PREFIX)) + throw new ArgumentException($"Key not valid. It should start with '{KEY_PREFIX}'."); + key = key.Substring(KEY_PREFIX.Length); if (key.StartsWith(TOPIC_PREFIX)) { @@ -78,7 +65,7 @@ public MessageKey(byte[] key) : base(null) if (optionIndexTag == 0x01) Index = reader.ReadUInt32(); - Key = MESSAGE_PREFIX + + Key = KEY_PREFIX + (Index.HasValue ? "" : TOPIC_PREFIX) + AddressableEntity.ToString() + "-" + TopicHash + diff --git a/Casper.Network.SDK/Types/GlobalStateKey/NamedKeyKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/NamedKeyKey.cs index b26afa3..bf70dc2 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/NamedKeyKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/NamedKeyKey.cs @@ -11,12 +11,12 @@ public class NamedKeyKey : GlobalStateKey private static string GetPrefix(string key) { - if (key.StartsWith(NAMEDKEYPREFIX+EntityKind.System.ToKeyPrefix())) - return NAMEDKEYPREFIX+EntityKind.System.ToKeyPrefix(); - if (key.StartsWith(NAMEDKEYPREFIX+EntityKind.Account.ToKeyPrefix())) - return NAMEDKEYPREFIX+EntityKind.Account.ToKeyPrefix(); - if (key.StartsWith(NAMEDKEYPREFIX+EntityKind.Contract.ToKeyPrefix())) - return NAMEDKEYPREFIX+EntityKind.Contract.ToKeyPrefix(); + if (key.StartsWith(NAMEDKEYPREFIX+EntityKindEnum.System.ToKeyPrefix())) + return NAMEDKEYPREFIX+EntityKindEnum.System.ToKeyPrefix(); + if (key.StartsWith(NAMEDKEYPREFIX+EntityKindEnum.Account.ToKeyPrefix())) + return NAMEDKEYPREFIX+EntityKindEnum.Account.ToKeyPrefix(); + if (key.StartsWith(NAMEDKEYPREFIX+EntityKindEnum.Contract.ToKeyPrefix())) + return NAMEDKEYPREFIX+EntityKindEnum.Contract.ToKeyPrefix(); throw new Exception("Unexpected key prefix in NamedKeyKey: " + key); } diff --git a/Casper.Network.SDK/Types/GlobalStateKey/PackageKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/PackageKey.cs index fa4d124..71b3a7c 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/PackageKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/PackageKey.cs @@ -1,4 +1,4 @@ -using Casper.Network.SDK.Utils; +using Org.BouncyCastle.Utilities.Encoders; namespace Casper.Network.SDK.Types { @@ -11,7 +11,7 @@ public PackageKey(string key) : base(key, KEYPREFIX) KeyIdentifier = KeyIdentifier.Package; } - public PackageKey(byte[] key) : this(KEYPREFIX + CEP57Checksum.Encode(key)) + public PackageKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } } diff --git a/Casper.Network.SDK/Types/IEntityIdentifier.cs b/Casper.Network.SDK/Types/IEntityIdentifier.cs new file mode 100644 index 0000000..289ae9c --- /dev/null +++ b/Casper.Network.SDK/Types/IEntityIdentifier.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Casper.Network.SDK.Types +{ + public interface IEntityIdentifier + { + public Dictionary GetEntityIdentifier(); + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/InitiatorAddr.cs b/Casper.Network.SDK/Types/InitiatorAddr.cs index 6a1e87d..a3f7b70 100644 --- a/Casper.Network.SDK/Types/InitiatorAddr.cs +++ b/Casper.Network.SDK/Types/InitiatorAddr.cs @@ -11,6 +11,7 @@ public class InitiatorAddr /// The public key of the initiator /// [JsonPropertyName("PublicKey")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonConverter(typeof(PublicKey.PublicKeyConverter))] public PublicKey PublicKey { get; init; } @@ -18,6 +19,7 @@ public class InitiatorAddr /// The account hash derived from the public key of the initiator /// [JsonPropertyName("AccountHash")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public AccountHashKey AccountHash { get; init; } diff --git a/Casper.Network.SDK/Types/Message.cs b/Casper.Network.SDK/Types/Message.cs new file mode 100644 index 0000000..bb2c3d4 --- /dev/null +++ b/Casper.Network.SDK/Types/Message.cs @@ -0,0 +1,81 @@ +using System; +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + /// + /// Summary of a message topic that will be stored in global state. + /// + public class MessageTopicSummary + { + /// + /// Block timestamp in which these messages were emitted. + /// + [JsonPropertyName("blocktime")] + public UInt64 BlockTime { get; init; } + + /// + /// Number of messages in this topic. + /// + [JsonPropertyName("message_count")] + public UInt32 MessageCount { get; init; } + } + + /// + /// The payload of a message. + /// + public class MessagePayload + { + /// + /// Human readable string message. + /// + public string String { get; init; } + /// + /// Message represented as raw bytes. + /// + public string Bytes { get; init; } + } + + /// + /// Message that was emitted by an addressable entity during execution. + /// + public class Message + { + /// + /// The identity of the entity that produced the message. + /// + [JsonPropertyName("entity_hash")] + [JsonConverter(typeof(AddressableEntityKey.AddressableEntityKeyConverter))] + public AddressableEntityKey AddressableEntity { get; init; } + + /// + /// The payload of the message. + /// + [JsonPropertyName("message")] + public MessagePayload MessagePayload { get; init; } + + /// + /// The name of the topic on which the message was emitted on. + /// + [JsonPropertyName("topic_name")] + public string TopicName { get; init; } + + /// + /// The hash of the name of the topic. + /// + [JsonPropertyName("topic_name_hash")] + public string TopicNameHash { get; init; } + + /// + /// Message index in the topic. + /// + [JsonPropertyName("topic_index")] + public uint TopicIndex { get; init; } + + /// + /// Message index in the block. + /// + [JsonPropertyName("block_index")] + public ulong BlockIndex { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/NamedKey.cs b/Casper.Network.SDK/Types/NamedKey.cs index f7a0db2..8d4b052 100644 --- a/Casper.Network.SDK/Types/NamedKey.cs +++ b/Casper.Network.SDK/Types/NamedKey.cs @@ -20,4 +20,22 @@ public class NamedKey [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public GlobalStateKey Key { get; init; } } + + /// + /// A named key in an addressable entity. + /// + public class NamedKeyValue + { + /// + /// The name of the entry. + /// + [JsonPropertyName("name")] + public CLValue Name { get; init; } + + /// + /// The value of the entry: a casper `Key` type. + /// + [JsonPropertyName("named_key")] + public CLValue Key { get; init; } + } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Package.cs b/Casper.Network.SDK/Types/Package.cs new file mode 100644 index 0000000..c142ffb --- /dev/null +++ b/Casper.Network.SDK/Types/Package.cs @@ -0,0 +1,107 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; + +namespace Casper.Network.SDK.Types +{ + /// + /// Major element of `ProtocolVersion` combined with `EntityVersion`. + /// + public class EntityVersion + { + /// + /// Automatically incremented value for a contract version within a major `ProtocolVersion`. + /// + [JsonPropertyName("entity_version")] + public uint Version { get; init; } + + /// + /// Major element of `ProtocolVersion` a `ContractVersion` is compatible with. + /// + [JsonPropertyName("protocol_version_major")] + public uint ProtocolVersionMajor { get; init; } + } + + public enum PackageStatus + { + /// + /// The package is locked and cannot be versioned. + /// + Locked, + /// + /// The package is unlocked and can be versioned. + /// + Unlocked, + } + + public class EntityVersionAndHash + { + /// + /// Major element of `ProtocolVersion` combined with `EntityVersion`. + /// + [JsonPropertyName("entity_version_key")] + public EntityVersion EntityVersion { get; init; } + + /// + /// The hex-encoded address of the addressable entity. + /// + [JsonPropertyName("addressable_entity_hash")] + public string AddressableEntityHash { get; init; } + } + + public class NamedUserGroup + { + /// + /// The hex-encoded address of the addressable entity. + /// + [JsonPropertyName("group_name")] + public string Name { get; init; } + + /// + /// List of URefs associated with the group. + /// + [JsonPropertyName("group_users")] + [JsonConverter(typeof(GenericListConverter))] + public List Users { get; init; } + } + + /// + /// Entity definition, metadata, and security container. + /// + public class Package + { + /// + /// Key used to add or disable versions. + /// + [JsonPropertyName("access_key")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public URef AccessKey { get; init; } + + /// + /// All versions (enabled & disabled). + /// + [JsonPropertyName("versions")] + public List Versions { get; init; } + + /// + /// Collection of disabled entity versions. The runtime will not permit disabled entity versions to be executed. + /// + [JsonPropertyName("disabled_versions")] + public List DisabledVersions { get; init; } + + /// + /// Mapping maintaining the set of URefs associated with each "user group". This can be used + /// to control access to methods in a particular version of the entity. A method is callable + /// by any context which "knows" any of the URefs associated with the method's user group. + /// + [JsonPropertyName("groups")] + public List Groups { get; init; } + + /// + /// The current state of the contract package. + /// + [JsonPropertyName("lock_status")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public LockStatus LockStatus { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/PricingMode.cs b/Casper.Network.SDK/Types/PricingMode.cs new file mode 100644 index 0000000..106c684 --- /dev/null +++ b/Casper.Network.SDK/Types/PricingMode.cs @@ -0,0 +1,161 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ +public enum PricingModeType + { + /// + /// The original payment model, where the creator of the transaction specifies how much they will pay, + /// at what gas price. + /// + Classic = 0, + + /// + /// The cost of the transaction is determined by the cost table, per the transaction category. + /// + Fixed = 1, + + /// + /// The payment for this transaction was previously reserved, as proven by the receipt hash + /// (this is for future use, not currently supported by the Casper network). + /// + Reserved = 2, + } + + /// + /// Pricing mode of a Transaction. + /// + public class PricingMode + { + /// + /// Pricing mode used: Classic, Fixed, Reserved. + /// + /// + public PricingModeType Type { get; init; } + + /// + /// Payment amount. + /// + public UInt64? PaymentAmount { get; set; } + + /// + /// User-specified gas_price tolerance (minimum 1). This is interpreted to mean "do not include this + /// transaction in a block if the current gas price is greater than this number". + /// + public UInt16? GasPriceTolerance { get; set; } + + /// + /// Standard payment. + /// + public bool? StandardPayment { get; set; } + + /// + /// Pre-paid receipt in the Reserved Pricing mode. + /// + public string Receipt { get; init; } + + public class PricingModeConverter : JsonConverter + { + public override PricingMode Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException("Cannot deserialize PricingMode. StartObject expected"); + reader.Read(); + + if (reader.TokenType != JsonTokenType.PropertyName) + throw new JsonException("Cannot deserialize PricingMode. PropertyName expected"); + + string pricingModeType = reader.GetString(); + reader.Read(); + + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException("Cannot deserialize PricingMode. StartObject expected"); + reader.Read(); + + UInt64? paymentAmount = null; + UInt16? gasPriceTolerance = null; + bool? standardPayment = null; + string receipt = null; + + while (reader.TokenType == JsonTokenType.PropertyName) + { + var field = reader.GetString(); + reader.Read(); + switch (field) + { + case "payment_amount": + paymentAmount = reader.GetUInt64(); + break; + case "standard_payment": + standardPayment = reader.GetBoolean(); + break; + case "gas_price_tolerance": + gasPriceTolerance = reader.GetUInt16(); + break; + case "receipt": + receipt = reader.GetString(); + break; + } + + reader.Read(); + } + + reader.Read(); + + return new PricingMode() + { + Type = EnumCompat.Parse(pricingModeType), + PaymentAmount = paymentAmount, + GasPriceTolerance = gasPriceTolerance, + StandardPayment = standardPayment, + Receipt = receipt, + }; + } + + public override void Write( + Utf8JsonWriter writer, + PricingMode value, + JsonSerializerOptions options) + { + if (value.Type == PricingModeType.Classic) + { + writer.WriteStartObject(); + writer.WritePropertyName("Classic"); + writer.WriteStartObject(); + if (value.PaymentAmount.HasValue) + writer.WriteNumber("payment_amount", value.PaymentAmount.Value); + if (value.GasPriceTolerance.HasValue) + writer.WriteNumber("gas_price_tolerance", value.GasPriceTolerance.Value); + if (value.StandardPayment.HasValue) + writer.WriteBoolean("standard_payment", value.StandardPayment.Value); + writer.WriteEndObject(); + writer.WriteEndObject(); + } + else if (value.Type == PricingModeType.Fixed) + { + writer.WriteStartObject(); + writer.WritePropertyName("Fixed"); + writer.WriteStartObject(); + if (value.GasPriceTolerance.HasValue) + writer.WriteNumber("gas_price_tolerance", value.GasPriceTolerance.Value); + writer.WriteEndObject(); + writer.WriteEndObject(); + } + else if (value.Type == PricingModeType.Reserved) + { + writer.WriteStartObject(); + writer.WritePropertyName("Reserved"); + writer.WriteStartObject(); + writer.WriteString("receipt", value.Receipt); + writer.WriteEndObject(); + writer.WriteEndObject(); + } + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/PublicKey.cs b/Casper.Network.SDK/Types/PublicKey.cs index 5d883b5..6aa8868 100644 --- a/Casper.Network.SDK/Types/PublicKey.cs +++ b/Casper.Network.SDK/Types/PublicKey.cs @@ -18,7 +18,7 @@ namespace Casper.Network.SDK.Types /// /// A wrapper for a Public Key. Provides signature verification functionality. /// - public class PublicKey: IPurseIdentifier + public class PublicKey: IPurseIdentifier, IEntityIdentifier { /// /// Byte array without the Key algorithm identifier. @@ -276,6 +276,17 @@ public Dictionary GetPurseIdentifier() {"main_purse_under_public_key", this.ToString()} }; } + + /// + /// Returns an EntityIdentifier object as defined in the RPC schema for a public key. + /// + public Dictionary GetEntityIdentifier() + { + return new Dictionary + { + {"PublicKey", this.ToString()} + }; + } #region Cast operators diff --git a/Casper.Network.SDK/Types/Reservation.cs b/Casper.Network.SDK/Types/Reservation.cs new file mode 100644 index 0000000..37333e4 --- /dev/null +++ b/Casper.Network.SDK/Types/Reservation.cs @@ -0,0 +1,19 @@ +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + /// + /// Container for bytes recording location, type and data for a gas reservation. + /// + public class Reservation + { + [JsonPropertyName("receipt")] + public string Receipt { get; init; } + + [JsonPropertyName("reservation_kind")] + public byte ReservationKind { get; init; } + + [JsonPropertyName("reservation_data")] + public string ReservationData { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Signature.cs b/Casper.Network.SDK/Types/Signature.cs index a28b27d..24799de 100644 --- a/Casper.Network.SDK/Types/Signature.cs +++ b/Casper.Network.SDK/Types/Signature.cs @@ -30,11 +30,8 @@ protected Signature(byte[] rawBytes, KeyAlgo keyAlgo) /// public static Signature FromHexString(string signature) { - var rawBytes = CEP57Checksum.Decode(signature.Substring(2), out var checksumResult); + var rawBytes = Hex.Decode(signature.Substring(2)); - if (checksumResult == CEP57Checksum.InvalidChecksum) - throw new ArgumentException("Signature checksum mismatch."); - KeyAlgo algo = signature.Substring(0, 2) switch { "01" => KeyAlgo.ED25519, @@ -92,9 +89,9 @@ public byte[] GetBytes() public string ToHexString() { if (KeyAlgorithm == KeyAlgo.ED25519) - return "01" + CEP57Checksum.Encode(RawBytes); + return "01" + Hex.ToHexString(RawBytes); else - return "02" + CEP57Checksum.Encode(RawBytes); + return "02" + Hex.ToHexString(RawBytes); } public override string ToString() diff --git a/Casper.Network.SDK/Types/StoredValue.cs b/Casper.Network.SDK/Types/StoredValue.cs index d52c9c5..1117fe2 100644 --- a/Casper.Network.SDK/Types/StoredValue.cs +++ b/Casper.Network.SDK/Types/StoredValue.cs @@ -9,6 +9,7 @@ namespace Casper.Network.SDK.Types /// /// A wrapper class for different types of values stored in the global state. /// + [JsonConverter(typeof(StoredValue.StoredValueConverter))] public class StoredValue { public Contract Contract { get; init; } @@ -21,16 +22,57 @@ public class StoredValue public ContractPackage ContractPackage { get; init; } - public Transfer Transfer { get; init; } + public Transfer LegacyTransfer { get; init; } public DeployInfo DeployInfo { get; init; } public EraInfo EraInfo { get; init; } public Bid Bid { get; init; } - - public List Withdraw { get; init; } - + + public BidKind BidKind { get; init; } + + public List Withdraw { get; init; } + + public List Unbonding { get; init; } + + /// + /// Stores an addressable entity. + /// + public AddressableEntity AddressableEntity { get; init; } + + /// + /// Stores a package. + /// + public Package Package { get; init; } + + /// + /// A record of byte code. + /// + public ByteCode ByteCode { get; init; } + + /// + /// Stores a message topic. + /// + public MessageTopicSummary MessageTopic { get; init; } + + /// + /// Stores a message checksum. + /// + public string Message { get; init; } + + /// + /// Stores a NamedKey. + /// + public NamedKeyValue NamedKey { get; init; } + + /// + /// Stores location, type and data for a gas reservation. + /// + public Reservation Reservation { get; init; } + + public EntryPoint EntryPoint { get; init; } + public class StoredValueConverter : JsonConverter { public override StoredValue Read diff --git a/Casper.Network.SDK/Types/Transaction.cs b/Casper.Network.SDK/Types/Transaction.cs new file mode 100644 index 0000000..9241aea --- /dev/null +++ b/Casper.Network.SDK/Types/Transaction.cs @@ -0,0 +1,22 @@ +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + /// + /// A versioned wrapper for a transaction or deploy. + /// + public class Transaction + { + /// + /// The deploy. + /// + [JsonPropertyName("Deploy")] + public Deploy Deploy { get; init; } + + /// + /// A version 1 transaction. + /// + [JsonPropertyName("Version1")] + public TransactionV1 TransactionV1 { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionEntryPoint.cs b/Casper.Network.SDK/Types/TransactionEntryPoint.cs new file mode 100644 index 0000000..fca8818 --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionEntryPoint.cs @@ -0,0 +1,116 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + public enum NativeEntryPoint + { + /// + /// The `transfer` native entry point, used to transfer `Motes` from a source purse to a target purse. + /// + Transfer = 1, + /// + /// The `add_bid` native entry point, used to create or top off a bid purse. + /// + AddBid = 2, + /// + /// The `withdraw_bid` native entry point, used to decrease a stake. + /// + WithdrawBid = 3, + /// + /// The `delegate` native entry point, used to add a new delegator or increase an existing delegator's stake. + /// + Delegate = 4, + /// + /// The `undelegate` native entry point, used to reduce a delegator's stake or remove the delegator if the remaining stake is 0. + /// + Undelegate = 5, + /// + /// The `redelegate` native entry point, used to reduce a delegator's stake or remove the delegator if + /// the remaining stake is 0, and after the unbonding delay, automatically delegate to a new validator. + /// + Redelegate = 6, + /// + /// The `activate_bid` native entry point, used to used to reactivate an inactive bid. + /// + ActivateBid = 7, + /// + /// The `change_bid_public_key` native entry point, used to change a bid's public key. + /// + ChangeBidPublicKey = 8, + /// + /// Used to call entry point call() in session transactions + /// + Call = 9, + } + + public class TransactionEntryPoint + { + public NativeEntryPoint? Native { get; init; } + + public string Custom { get; init; } + + public TransactionEntryPoint(NativeEntryPoint name) + { + Native = name; + Custom = null; + } + + public TransactionEntryPoint(string customEntryPoint) + { + Native = null; + Custom = customEntryPoint; + } + + public class TransactionEntryPointConverter : JsonConverter + { + public override TransactionEntryPoint Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + var nativeEntryPoint = EnumCompat.Parse(reader.GetString()); + return new TransactionEntryPoint(nativeEntryPoint); + } + + if (reader.TokenType == JsonTokenType.StartObject) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.PropertyName && + reader.GetString() == "Custom") + { + reader.Read(); + var customEntryPoint = reader.GetString(); + reader.Read(); + return new TransactionEntryPoint(customEntryPoint); + } + } + + throw new JsonException("Cannot deserialize TransactionEntryPoint."); + } + + public override void Write( + Utf8JsonWriter writer, + TransactionEntryPoint value, + JsonSerializerOptions options) + { + if (value.Native.HasValue) + { + writer.WriteStringValue(value.Native.Value.ToString()); + } + else if (value.Custom != null) + { + writer.WriteStartObject(); + writer.WritePropertyName("Custom"); + writer.WriteStringValue(value.Custom); + writer.WriteEndObject(); + } + else + throw new JsonException("Cannot serialize empty transaction entry point."); + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionHash.cs b/Casper.Network.SDK/Types/TransactionHash.cs index 7b5e26b..391a09e 100644 --- a/Casper.Network.SDK/Types/TransactionHash.cs +++ b/Casper.Network.SDK/Types/TransactionHash.cs @@ -11,4 +11,4 @@ public override string ToString() return Deploy ?? Version1; } } -} \ No newline at end of file +} diff --git a/Casper.Network.SDK/Types/TransactionScheduling.cs b/Casper.Network.SDK/Types/TransactionScheduling.cs new file mode 100644 index 0000000..c6ea9b6 --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionScheduling.cs @@ -0,0 +1,105 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Utils; + +namespace Casper.Network.SDK.Types +{ + public enum TransactionSchedulingType + { + Standard = 0, + FutureEra = 1, + FutureTimestamp = 2, + } + + public class TransactionScheduling + { + public TransactionSchedulingType Type { get; init; } + + public ulong EraId { get; init; } + + public ulong Timestamp { get; init; } + + public class TransactionSchedulingConverter : JsonConverter + { + public override TransactionScheduling Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + TransactionScheduling scheduling; + + if (reader.TokenType == JsonTokenType.String) + { + var schedulingType = reader.GetString(); + switch (schedulingType) + { + case "Standard": + scheduling = new TransactionScheduling + { + Type = TransactionSchedulingType.Standard, + }; + break; + default: + throw new JsonException("Cannot deserialize TransactionScheduling. Unknown scheduling type"); + } + } + else if (reader.TokenType == JsonTokenType.StartObject) + { + reader.Read(); // skip start object + var schedulingType = reader.GetString(); + reader.Read(); + switch (schedulingType) + { + case "FutureEra": + scheduling = new TransactionScheduling + { + Type = TransactionSchedulingType.FutureEra, + EraId = reader.GetUInt64(), + }; + break; + case "FutureTimestamp": + scheduling = new TransactionScheduling + { + Type = TransactionSchedulingType.FutureTimestamp, + Timestamp = reader.GetUInt64(), + }; + break; + default: + throw new JsonException("Cannot deserialize TransactionScheduling. Unknown scheduling type"); + } + reader.Read(); + } + else + throw new JsonException("Cannot deserialize TransactionScheduling."); + + return scheduling; + } + + public override void Write( + Utf8JsonWriter writer, + TransactionScheduling value, + JsonSerializerOptions options) + { + switch (value.Type) + { + case TransactionSchedulingType.Standard: + writer.WriteStringValue("Standard"); + break; + case TransactionSchedulingType.FutureEra: + writer.WriteStartObject(); + writer.WriteNumber("FutureEra", value.EraId); + writer.WriteEndObject(); + break; + case TransactionSchedulingType.FutureTimestamp: + writer.WriteStartObject(); + writer.WriteString("FutureTimestamp", DateUtils.ToISOString(value.Timestamp)); + writer.WriteEndObject(); + break; + default: + throw new JsonException("Cannot serialize due to unkown transaction scheduling type."); + } + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionTarget.cs b/Casper.Network.SDK/Types/TransactionTarget.cs new file mode 100644 index 0000000..31cc88e --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionTarget.cs @@ -0,0 +1,331 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; +using Org.BouncyCastle.Tls.Crypto.Impl.BC; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Casper.Network.SDK.Types +{ + public enum InvocationTargetTag + { + ByHash = 0, + ByName = 1, + ByPackageHash = 2, + ByPackageName = 3, + } + + public class ByHashInvocationTarget : IInvocationTarget + { + public string Hash { get; init; } + } + + public class ByNameInvocationTarget : IInvocationTarget + { + public string Name { get; init; } + } + + public class ByPackageHashInvocationTarget : IInvocationTarget + { + [JsonPropertyName("addr")] public string Addr { get; init; } + + [JsonPropertyName("version")] public UInt32? Version { get; init; } + } + + public class ByPackageNameInvocationTarget : IInvocationTarget + { + [JsonPropertyName("name")] public string Name { get; init; } + + [JsonPropertyName("version")] public UInt32? Version { get; init; } + } + + [JsonConverter(typeof(InvocationTargetConverter))] + public interface IInvocationTarget + { + public class InvocationTargetConverter : JsonConverter + { + public override IInvocationTarget Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + IInvocationTarget target; + + if (reader.TokenType == JsonTokenType.StartObject) + { + reader.Read(); // skip start object + var idType = reader.GetString(); + reader.Read(); + switch (idType) + { + case "ByHash": + target = new ByHashInvocationTarget { Hash = reader.GetString() }; + reader.Read(); + break; + case "ByName": + target = new ByNameInvocationTarget { Name = reader.GetString() }; + reader.Read(); + break; + case "ByPackageHash": + target = JsonSerializer.Deserialize(ref reader, options); + break; + case "ByPackageName": + target = JsonSerializer.Deserialize(ref reader, options); + break; + default: + throw new JsonException( + "Cannot deserialize TransactionScheduling. Unknown scheduling type"); + } + + reader.Read(); // skip end object + } + else + throw new JsonException("Cannot deserialize TransactionScheduling."); + + return target; + } + + public override void Write( + Utf8JsonWriter writer, + IInvocationTarget value, + JsonSerializerOptions options) + { + writer.WriteStartObject(); + + if (value is ByHashInvocationTarget byHash) + writer.WriteString("ByHash", byHash.Hash); + else if (value is ByNameInvocationTarget byName) + writer.WriteString("ByName", byName.Name); + else if (value is ByPackageHashInvocationTarget byPackageHash) + { + writer.WritePropertyName("ByPackageHash"); + JsonSerializer.Serialize(writer, byPackageHash); + } + else if (value is ByPackageNameInvocationTarget byPackageName) + { + writer.WritePropertyName("ByPackageName"); + JsonSerializer.Serialize(writer, byPackageName); + } + else + throw new JsonException("Unknown invocation target type."); + + writer.WriteEndObject(); + } + } + } + + public enum TransactionTargetType + { + Native = 0, + Stored = 1, + Session = 2, + } + + public enum TransactionRuntime + { + /// + /// The Casper Version 1 Virtual Machine. + /// + VmCasperV1, + + /// + /// The Casper Version 2 Virtual Machine. + /// + VmCasperV2, + } + + public class TransactionTarget + { + public TransactionTargetType Type { get; init; } + + [JsonPropertyName("id")] public IInvocationTarget Id { get; init; } + + /// + /// wasm Bytes + /// + [JsonPropertyName("module_bytes")] + [JsonConverter(typeof(HexBytesConverter))] + public byte[] ModuleBytes { get; init; } + + [JsonPropertyName("runtime")] public TransactionRuntime Runtime { get; set; } + + public static TransactionTarget StoredByHash(string hash) + { + return new TransactionTarget() + { + Type = TransactionTargetType.Stored, + Id = new ByHashInvocationTarget { Hash = hash } + }; + } + + public static TransactionTarget StoredByName(string name) + { + return new TransactionTarget() + { + Type = TransactionTargetType.Stored, + Id = new ByNameInvocationTarget { Name = name } + }; + } + + public static TransactionTarget StoredByPackageHash(string hash, UInt32? version = null) + { + return new TransactionTarget() + { + Type = TransactionTargetType.Stored, + Id = new ByPackageHashInvocationTarget { Addr = hash, Version = version } + }; + } + + public static TransactionTarget StoredByPackageName(string name, UInt32? version = null) + { + return new TransactionTarget() + { + Type = TransactionTargetType.Stored, + Id = new ByPackageNameInvocationTarget() { Name = name, Version = version } + }; + } + + public static TransactionTarget Session(byte[] moduleBytes) + { + return new TransactionTarget() + { + Type = TransactionTargetType.Session, + ModuleBytes = moduleBytes, + }; + } + + public class TransactionTargetConverter : JsonConverter + { + public override TransactionTarget Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + var targetType = reader.GetString(); + + var type = EnumCompat.Parse(targetType); + switch (targetType) + { + case "Native": + return new TransactionTarget + { + Type = TransactionTargetType.Native, + }; + default: + throw new JsonException($"TransactionTargetType '{targetType}' not supported."); + } + } + else if (reader.TokenType == JsonTokenType.StartObject) + { + TransactionTarget transactionTarget = null; + IInvocationTarget id = null; + string module_bytes = null; + string runtime = null; + + reader.Read(); // skip start object + var targetType = reader.GetString(); + reader.Read(); + + switch (targetType) + { + case "Stored": + reader.Read(); + while (reader.TokenType != JsonTokenType.EndObject) + { + var prop = reader.GetString(); + reader.Read(); + switch (prop) + { + case "id": + id = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + break; + case "runtime": + runtime = reader.GetString(); + reader.Read(); // skip end object + break; + } + } + + reader.Read(); // skip end object + + transactionTarget = new TransactionTarget() + { + Type = EnumCompat.Parse(targetType), + Id = id, + }; + if (runtime != null) + transactionTarget.Runtime = EnumCompat.Parse(runtime); + break; + case "Session": + reader.Read(); + while (reader.TokenType != JsonTokenType.EndObject) + { + var prop = reader.GetString(); + reader.Read(); + switch (prop) + { + case "module_bytes": + module_bytes = reader.GetString(); + break; + case "runtime": + runtime = reader.GetString(); + break; + } + } + + reader.Read(); // skip end object + + transactionTarget = new TransactionTarget() + { + Type = EnumCompat.Parse(targetType), + ModuleBytes = Hex.Decode(module_bytes), + Runtime = EnumCompat.Parse(runtime), + }; + break; + default: + throw new JsonException($"TransactionTargetType '{targetType}' not supported."); + } + + return transactionTarget; + } + + throw new JsonException("Cannot deserialize TransactionTarget. PropertyName expected"); + } + + public override void Write( + Utf8JsonWriter writer, + TransactionTarget value, + JsonSerializerOptions options) + { + switch (value.Type) + { + case TransactionTargetType.Native: + writer.WriteStringValue("Native"); + break; + case TransactionTargetType.Stored: + writer.WriteStartObject(); + writer.WriteStartObject("Stored"); + writer.WritePropertyName("id"); + JsonSerializer.Serialize(writer, value.Id); + writer.WriteString("runtime", value.Runtime.ToString()); + writer.WriteEndObject(); + writer.WriteEndObject(); + break; + case TransactionTargetType.Session: + writer.WriteStartObject(); + writer.WriteStartObject("Session"); + writer.WriteString("module_bytes", Hex.ToHexString(value.ModuleBytes)); + writer.WriteString("runtime", value.Runtime.ToString()); + writer.WriteEndObject(); + writer.WriteEndObject(); + break; + default: + throw new JsonException("Cannot serialize empty transaction target."); + } + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionV1.cs b/Casper.Network.SDK/Types/TransactionV1.cs new file mode 100644 index 0000000..7990462 --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionV1.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using Casper.Network.SDK.ByteSerializers; +using Casper.Network.SDK.Utils; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Casper.Network.SDK.Types +{ + /// + /// A unit of work sent by a client to the network, which when executed can cause global state to be altered. + /// + public class TransactionV1 + { + /// + /// A hash over the header of the transaction. + /// + [JsonPropertyName("hash")] + public string Hash { get; } + + /// + /// List of signers and signatures for this transaction. + /// + [JsonPropertyName("approvals")] + public List Approvals { get; } = new List(); + + /// + /// Header for this transaction. + /// + [JsonPropertyName("header")] + public TransactionV1Header Header { get; init; } + + /// + /// Body for this transaction. + /// + [JsonPropertyName("body")] + public TransactionV1Body Body { get; init; } + + /// + /// Loads and deserializes a TransactionV1 from a file. + /// + public static TransactionV1 Load(string filename) + { + var data = File.ReadAllText(filename); + return TransactionV1.Parse(data); + } + + /// + /// Parses a Transaction from a string with json. + /// + public static TransactionV1 Parse(string json) + { + try + { + var transaction = JsonSerializer.Deserialize(json); + + return transaction; + } + catch (JsonException e) + { + var message = $"The JSON value could not be converted to a TransactionV1 object. " + + $"{e.Message} Path: {e.Path} | LineNumber: {e.LineNumber} | " + + $"BytePositionInLine: {e.BytePositionInLine}."; + throw new Exception(message); + } + } + + /// + /// Saves a transaction object to a file. + /// + public void Save(string filename) + { + File.WriteAllText(filename, JsonSerializer.Serialize(this)); + } + + /// + /// Returns a json string with the transaction. + /// + public string SerializeToJson() + { + return JsonSerializer.Serialize(this); + } + + [JsonConstructor] + public TransactionV1(string hash, + TransactionV1Header header, + TransactionV1Body body, + List approvals) + { + this.Hash = hash; + this.Header = header; + this.Body = body; + this.Approvals = approvals; + } + + public TransactionV1(TransactionV1Header header, + TransactionV1Body body) + { + var bodyHash = ComputeBodyHash(body); + this.Header = new TransactionV1Header() + { + ChainName = header.ChainName, + Timestamp = header.Timestamp, + Ttl = header.Ttl, + BodyHash = Hex.ToHexString(bodyHash), + PricingMode = header.PricingMode, + InitiatorAddr = header.InitiatorAddr, + }; + this.Hash = Hex.ToHexString(ComputeHeaderHash(this.Header)); + this.Body = body; + } + + /// + /// Signs the transaction with a private key and adds a new Approval to it. + /// + public void Sign(KeyPair keyPair) + { + byte[] signature = keyPair.Sign(Hex.Decode(this.Hash)); + + Approvals.Add(new Approval() + { + Signature = Signature.FromRawBytes(signature, keyPair.PublicKey.KeyAlgorithm), + Signer = keyPair.PublicKey + }); + } + + /// + /// Adds an approval to the transaction. No check is done to the approval signature. + /// + public void AddApproval(Approval approval) + { + this.Approvals.Add(approval); + } + + /// + /// Validates the body and header hashes in the transaction. + /// + /// output string with a validation error message if validation fails. empty otherwise. + /// false if the validation of hashes is not successful + public bool ValidateHashes(out string message) + { + var computedHash = ComputeBodyHash(this.Body); + if (!Hex.Decode(this.Header.BodyHash).SequenceEqual(computedHash)) + { + message = "Computed Body Hash does not match value in transaction header. " + + $"Expected: '{this.Header.BodyHash}'. " + + $"Computed: '{computedHash}'."; + return false; + } + + computedHash = ComputeHeaderHash(this.Header); + if (!Hex.Decode(this.Hash).SequenceEqual(computedHash)) + { + message = "Computed Hash does not match value in transaction object. " + + $"Expected: '{this.Hash}'. " + + $"Computed: '{computedHash}'."; + return false; + } + + message = ""; + return true; + } + + /// + /// Verifies the signatures in the list of approvals. + /// + /// an output string with the signer which signature could not be verified. empty if verification succeeds. + /// false if the verification of a signature fails. + public bool VerifySignatures(out string message) + { + message = string.Empty; + + foreach (var approval in Approvals) + { + if (!approval.Signer.VerifySignature(Hex.Decode(this.Hash), + approval.Signature.RawBytes)) + { + message = $"Error verifying signature with signer '{approval.Signer}'."; + return false; + } + } + + return true; + } + + /// + /// returns the number of bytes resulting from the binary serialization of the Deploy. + /// + public int GetTransactionSizeInBytes() + { + var serializer = new TransactionV1ByteSerializer(); + return serializer.ToBytes(this).Length; + } + + private byte[] ComputeBodyHash(TransactionV1Body body) + { + var ms = new MemoryStream(); + + var serializer = new TransactionV1ByteSerializer(); + + ms.Write(serializer.ToBytes(body)); + + var bcBl2bdigest = new Org.BouncyCastle.Crypto.Digests.Blake2bDigest(256); + var bBody = ms.ToArray(); + + bcBl2bdigest.BlockUpdate(bBody, 0, bBody.Length); + + var hash = new byte[bcBl2bdigest.GetDigestSize()]; + bcBl2bdigest.DoFinal(hash, 0); + + return hash; + } + + private byte[] ComputeHeaderHash(TransactionV1Header header) + { + var serializer = new TransactionV1ByteSerializer(); + var bHeader = serializer.ToBytes(header); + + var bcBl2bdigest = new Org.BouncyCastle.Crypto.Digests.Blake2bDigest(256); + + bcBl2bdigest.BlockUpdate(bHeader, 0, bHeader.Length); + + var hash = new byte[bcBl2bdigest.GetDigestSize()]; + bcBl2bdigest.DoFinal(hash, 0); + + return hash; + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionV1Body.cs b/Casper.Network.SDK/Types/TransactionV1Body.cs new file mode 100644 index 0000000..65d4a38 --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionV1Body.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; + +namespace Casper.Network.SDK.Types +{ + /// + /// Body of a TransactionV1. + /// + public class TransactionV1Body + { + /// + /// List of runtime arguments. + /// + [JsonPropertyName("args")] + [JsonConverter(typeof(GenericListConverter))] + public List RuntimeArgs { get; init; } + + /// + /// Entry point or method of the contract to call. + /// + [JsonPropertyName("entry_point")] + [JsonConverter(typeof(TransactionEntryPoint.TransactionEntryPointConverter))] + public TransactionEntryPoint EntryPoint { get; init; } + + /// + /// Target of the transaction (native, custom or module_bytes). + /// + [JsonPropertyName("target")] + [JsonConverter(typeof(TransactionTarget.TransactionTargetConverter))] + public TransactionTarget Target { get; init; } + + /// + /// Scheduling of the transaction.. + /// + [JsonPropertyName("scheduling")] + [JsonConverter(typeof(TransactionScheduling.TransactionSchedulingConverter))] + public TransactionScheduling Scheduling { get; init; } + + [JsonPropertyName("transaction_category")] + public byte TransactionCategory { get; init; } + } +} diff --git a/Casper.Network.SDK/Types/TransactionV1Header.cs b/Casper.Network.SDK/Types/TransactionV1Header.cs new file mode 100644 index 0000000..8b30fdc --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionV1Header.cs @@ -0,0 +1,53 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; +using Casper.Network.SDK.Utils; + +namespace Casper.Network.SDK.Types +{ + /// + /// The header portion of a TransactionV1. + /// + public class TransactionV1Header + { + /// + /// The address of the initiator of a transaction. + /// + [JsonPropertyName("initiator_addr")] + public InitiatorAddr InitiatorAddr { get; set; } + + /// + /// Timestamp formatted as per RFC 3339 + /// + [JsonPropertyName("timestamp")] + [JsonConverter(typeof(DateTime2EpochConverter))] + public ulong Timestamp { get; set; } + + /// + /// Duration of the Deploy in milliseconds (from timestamp). + /// + [JsonPropertyName("ttl")] + [JsonConverter(typeof(HumanizeTTLConverter))] + public ulong Ttl { get; set; } + + /// + /// Pricing mode of a Transaction. + /// + [JsonPropertyName("pricing_mode")] + [JsonConverter(typeof(PricingMode.PricingModeConverter))] + public PricingMode PricingMode { get; set; } + + /// + /// Hash of the body part of this Deploy. + /// + [JsonPropertyName("body_hash")] + public string BodyHash { get; set; } + + /// + /// Name of the chain where the deploy is executed. + /// + [JsonPropertyName("chain_name")] + public string ChainName { get; set; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Transform.cs b/Casper.Network.SDK/Types/Transform.cs index e9ee8e9..931ca2f 100644 --- a/Casper.Network.SDK/Types/Transform.cs +++ b/Casper.Network.SDK/Types/Transform.cs @@ -1,35 +1,76 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Numerics; using System.Text.Json; using System.Text.Json.Serialization; namespace Casper.Network.SDK.Types { - /// - /// Enumeration of transformation types used in the execution of a deploy. - /// - public enum TransformType + public enum TransformKind { + /// + /// An identity transformation that does not modify a value in the global state. Created as a result of + /// reading from the global state. + /// Identity, - WriteContractWasm, - WriteContract, - WriteContractPackage, - WriteCLValue, - WriteAccount, - WriteDeployInfo, - WriteEraInfo, - WriteTransfer, - WriteBid, - WriteWithdraw, + + /// + /// Writes a new value (StoredValue) in the global state. + /// + Write, + + /// + /// A wrapping addition of an `i32` to an existing numeric value (not necessarily an `i32`) in the global state. + /// AddInt32, + + /// + /// A wrapping addition of a `u64` to an existing numeric value (not necessarily an `u64`) in the global state. + /// AddUInt64, + + /// + /// A wrapping addition of a `U128` to an existing numeric value (not necessarily an `U128`) in the global state. + /// AddUInt128, + + /// + /// A wrapping addition of a `U256` to an existing numeric value (not necessarily an `U256`) in the global state. + /// AddUInt256, + + /// + /// A wrapping addition of a `U512` to an existing numeric value (not necessarily an `U512`) in the global state. + /// AddUInt512, + + /// + /// Adds new named keys to an existing entry in the global state.\n\nThis transform assumes that the existing stored + /// value is either an Account or a Contract. + /// AddKeys, + + /// + /// Removes the pathing to the global state entry of the specified key. The pruned element remains reachable from + /// previously generated global state root hashes, but will not be included in the next generated global state + /// root hash and subsequent state accumulated from it. + /// + Prune, + + /// + /// Represents the case where applying a transform would cause an error. + /// Failure, - WriteUnbonding, + } + + /// + /// Representation of a single transformation occurring during execution.\n\nNote that all arithmetic + /// variants of `TransformKindV2` are commutative which means that a given collection of them can be + /// executed in any order to produce the same end result. + /// + public class Kind + { } /// @@ -37,6 +78,62 @@ public enum TransformType /// public class Transform { + protected int _version; + + /// + /// Returns the version of the block. + /// + public int Version + { + get { return _version; } + } + + protected TransformV1 _transformV1; + + public static explicit operator TransformV1(Transform transform) + { + if(transform._version == 1) + return transform._transformV1; + + throw new InvalidCastException("Version2 transform cannot be converted to Version1"); + } + + public static explicit operator Transform(TransformV1 transform) + { + TransformKind kind = transform.Kind switch + { + TransformKindV1.Identity => TransformKind.Identity, + TransformKindV1.WriteAccount => TransformKind.Write, + TransformKindV1.WriteAddressableEntity => TransformKind.Write, + TransformKindV1.WriteBid => TransformKind.Write, + TransformKindV1.WriteBidKind => TransformKind.Write, + TransformKindV1.WriteCLValue => TransformKind.Write, + TransformKindV1.WriteContract => TransformKind.Write, + TransformKindV1.WriteContractPackage => TransformKind.Write, + TransformKindV1.WriteContractWasm => TransformKind.Write, + TransformKindV1.WriteDeployInfo => TransformKind.Write, + TransformKindV1.WriteEraInfo => TransformKind.Write, + TransformKindV1.WriteTransfer => TransformKind.Write, + TransformKindV1.WriteUnbonding => TransformKind.Write, + TransformKindV1.WriteWithdraw => TransformKind.Write, + TransformKindV1.AddInt32 => TransformKind.AddInt32, + TransformKindV1.AddUInt64 => TransformKind.AddUInt64, + TransformKindV1.AddUInt128 => TransformKind.AddUInt128, + TransformKindV1.AddUInt256 => TransformKind.AddUInt256, + TransformKindV1.AddUInt512 => TransformKind.AddUInt512, + TransformKindV1.AddKeys => TransformKind.AddKeys, + TransformKindV1.Failure => TransformKind.Failure, + TransformKindV1.Prune => TransformKind.Prune, + }; + + return new Transform + { + Key = transform.Key, + TransformKind = kind, + Value = transform.Value, + }; + } + /// /// The formatted string of the `Key`. /// @@ -44,9 +141,11 @@ public class Transform public GlobalStateKey Key { get; init; } /// - /// The type of transform + /// Representation of a single transformation occurring during execution.\n\nNote that all arithmetic + /// variants of `TransformKind` are commutative which means that a given collection of them can be + /// executed in any order to produce the same end result. /// - public TransformType Type { get; init; } + public TransformKind TransformKind { get; init; } /// /// Data associated to some type of transforms @@ -66,7 +165,7 @@ public override Transform Read( reader.Read(); // start object string key = null; - TransformType? type = null; + TransformKind? kind = null; object value = null; while (reader.TokenType == JsonTokenType.PropertyName) @@ -78,13 +177,13 @@ public override Transform Read( key = reader.GetString(); reader.Read(); } - else if (field == "transform") + else if (field == "kind") { if (reader.TokenType == JsonTokenType.String) { var stype = reader.GetString(); if (stype != null) - type = EnumCompat.Parse(stype); + kind = EnumCompat.Parse(stype); reader.Read(); } else if (reader.TokenType == JsonTokenType.StartObject) @@ -92,68 +191,44 @@ public override Transform Read( reader.Read(); var stype = reader.GetString(); if (stype != null) - type = EnumCompat.Parse(stype); + kind = EnumCompat.Parse(stype); reader.Read(); - switch (type) + switch (kind) { - case TransformType.WriteCLValue: - value = JsonSerializer.Deserialize(ref reader, options); + case TransformKind.Write: + value = JsonSerializer.Deserialize(ref reader, options); reader.Read(); // end object break; - case TransformType.WriteAccount: - value = GlobalStateKey.FromString(reader.GetString()); - reader.Read(); // end object - break; - case TransformType.WriteDeployInfo: - value = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); // end object - break; - case TransformType.WriteEraInfo: - value = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); // end object - break; - case TransformType.WriteTransfer: - value = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); // end object - break; - case TransformType.WriteBid: - value = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); // end object - break; - case TransformType.WriteWithdraw: - value = JsonSerializer.Deserialize>(ref reader, options); - reader.Read(); // end object - break; - case TransformType.AddInt32: + case TransformKind.AddInt32: value = reader.GetInt32(); reader.Read(); break; - case TransformType.AddUInt64: + case TransformKind.AddUInt64: value = reader.GetUInt64(); reader.Read(); break; - case TransformType.AddUInt128: + case TransformKind.AddUInt128: value = BigInteger.Parse(reader.GetString() ?? "0"); reader.Read(); break; - case TransformType.AddUInt256: + case TransformKind.AddUInt256: value = BigInteger.Parse(reader.GetString() ?? "0"); reader.Read(); break; - case TransformType.AddUInt512: + case TransformKind.AddUInt512: value = BigInteger.Parse(reader.GetString() ?? "0"); reader.Read(); break; - case TransformType.AddKeys: - value = JsonSerializer.Deserialize>(ref reader, options); + case TransformKind.AddKeys: + value = JsonSerializer.Deserialize>(ref reader, options); reader.Read(); // end array break; - case TransformType.Failure: - value = reader.GetString(); + case TransformKind.Prune: + value = GlobalStateKey.FromString(reader.GetString()); reader.Read(); break; - case TransformType.WriteUnbonding: - value = JsonSerializer.Deserialize>(ref reader, options); + case TransformKind.Failure: + value = reader.GetString(); reader.Read(); break; } @@ -163,12 +238,12 @@ public override Transform Read( } } - if (key != null & type != null) + if (key != null & kind != null) { return new Transform() { Key = GlobalStateKey.FromString(key), - Type = type ?? TransformType.Identity, + TransformKind = kind ?? TransformKind.Identity, Value = value }; } @@ -181,8 +256,8 @@ public override void Write( Transform value, JsonSerializerOptions options) { - throw new NotImplementedException("Write method for Transform not yet implemented"); + throw new NotImplementedException("Write method for TransformV2 not yet implemented"); } } } -} +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransformV1.cs b/Casper.Network.SDK/Types/TransformV1.cs new file mode 100644 index 0000000..7f638b5 --- /dev/null +++ b/Casper.Network.SDK/Types/TransformV1.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + /// + /// Enumeration of transformation types used in the execution of a deploy. + /// + public enum TransformKindV1 + { + Identity, + WriteContractWasm, + WriteContract, + WriteContractPackage, + WriteCLValue, + WriteAccount, + WriteDeployInfo, + WriteEraInfo, + WriteTransfer, + WriteBid, + WriteWithdraw, + AddInt32, + AddUInt64, + AddUInt128, + AddUInt256, + AddUInt512, + AddKeys, + Failure, + WriteUnbonding, + WriteAddressableEntity, + Prune, + WriteBidKind, + } + + /// + /// A transformation performed while executing a deploy. + /// + public class TransformV1 + { + /// + /// The formatted string of the `Key`. + /// + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public GlobalStateKey Key { get; init; } + + /// + /// The type of transform + /// + public TransformKindV1 Kind { get; init; } + + /// + /// Data associated to some type of transforms + /// + public object Value { get; init; } + + public class TransformV1Converter : JsonConverter + { + public override TransformV1 Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException("Cannot deserialize Transform. StartObject expected"); + + reader.Read(); // start object + + string key = null; + TransformKindV1? type = null; + object value = null; + + while (reader.TokenType == JsonTokenType.PropertyName) + { + var field = reader.GetString(); + reader.Read(); + if (field == "key") + { + key = reader.GetString(); + reader.Read(); + } + else if (field == "transform") + { + if (reader.TokenType == JsonTokenType.String) + { + var stype = reader.GetString(); + if (stype != null) + type = EnumCompat.Parse(stype); + reader.Read(); + } + else if (reader.TokenType == JsonTokenType.StartObject) + { + reader.Read(); + var stype = reader.GetString(); + if (stype != null) + type = EnumCompat.Parse(stype); + reader.Read(); + switch (type) + { + case TransformKindV1.WriteCLValue: + value = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); // end object + break; + case TransformKindV1.WriteAccount: + value = GlobalStateKey.FromString(reader.GetString()); + reader.Read(); // end object + break; + case TransformKindV1.WriteDeployInfo: + value = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); // end object + break; + case TransformKindV1.WriteEraInfo: + value = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); // end object + break; + case TransformKindV1.WriteTransfer: + value = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); // end object + break; + case TransformKindV1.WriteBid: + value = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); // end object + break; + case TransformKindV1.WriteWithdraw: + value = JsonSerializer.Deserialize>(ref reader, options); + reader.Read(); // end object + break; + case TransformKindV1.AddInt32: + value = reader.GetInt32(); + reader.Read(); + break; + case TransformKindV1.AddUInt64: + value = reader.GetUInt64(); + reader.Read(); + break; + case TransformKindV1.AddUInt128: + value = BigInteger.Parse(reader.GetString() ?? "0"); + reader.Read(); + break; + case TransformKindV1.AddUInt256: + value = BigInteger.Parse(reader.GetString() ?? "0"); + reader.Read(); + break; + case TransformKindV1.AddUInt512: + value = BigInteger.Parse(reader.GetString() ?? "0"); + reader.Read(); + break; + case TransformKindV1.AddKeys: + value = JsonSerializer.Deserialize>(ref reader, options); + reader.Read(); // end array + break; + case TransformKindV1.Failure: + value = reader.GetString(); + reader.Read(); + break; + case TransformKindV1.WriteUnbonding: + value = JsonSerializer.Deserialize>(ref reader, options); + reader.Read(); + break; + case TransformKindV1.Prune: + value = GlobalStateKey.FromString(reader.GetString()); + reader.Read(); + break; + case TransformKindV1.WriteBidKind: + value = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + break; + } + + reader.Read(); //end object + } + } + } + + if (key != null & type != null) + { + return new TransformV1() + { + Key = GlobalStateKey.FromString(key), + Kind = type ?? TransformKindV1.Identity, + Value = value + }; + } + + throw new JsonException("Incomplete transform"); + } + + public override void Write( + Utf8JsonWriter writer, + TransformV1 value, + JsonSerializerOptions options) + { + throw new NotImplementedException("Write method for Transform not yet implemented"); + } + } + } +} diff --git a/Casper.Network.SDK/Types/URef.cs b/Casper.Network.SDK/Types/URef.cs index bc3afc8..9792bb5 100644 --- a/Casper.Network.SDK/Types/URef.cs +++ b/Casper.Network.SDK/Types/URef.cs @@ -35,10 +35,6 @@ public URef(string value) : base(value) throw new ArgumentOutOfRangeException(nameof(value), "An URef object must contain a 3 digits access rights suffix."); - CEP57Checksum.Decode(parts[0], out int checksumResult); - if (checksumResult == CEP57Checksum.InvalidChecksum) - throw new ArgumentException("URef checksum mismatch."); - AccessRights = (AccessRights) uint.Parse(parts[1]); } @@ -76,7 +72,7 @@ public override byte[] GetBytes() public override string ToString() { - return KEYPREFIX + CEP57Checksum.Encode(RawBytes) + $"-{(byte) AccessRights:000}"; + return KEYPREFIX + Hex.ToHexString(RawBytes) + $"-{(byte) AccessRights:000}"; } /// diff --git a/Casper.Network.SDK/Types/UnbondingPurse.cs b/Casper.Network.SDK/Types/UnbondingPurse.cs index dfe3557..03ced7d 100644 --- a/Casper.Network.SDK/Types/UnbondingPurse.cs +++ b/Casper.Network.SDK/Types/UnbondingPurse.cs @@ -5,9 +5,9 @@ namespace Casper.Network.SDK.Types { /// - /// Information of an unbonding or delegation withdrawal + /// Information of a delegation withdrawal (legacy structure) /// - public class UnbondingPurse + public class WithdrawPurse { /// /// Unbonding Amount. @@ -39,6 +39,13 @@ public class UnbondingPurse [JsonPropertyName("validator_public_key")] [JsonConverter(typeof(PublicKey.PublicKeyConverter))] public PublicKey ValidatorPublicKey { get; init; } + } + + /// + /// Information of an unbonding or delegation withdrawal + /// + public class UnbondingPurse : WithdrawPurse + { /// /// The validator public key to re-delegate to. diff --git a/Casper.Network.SDK/Types/ValidatorBid.cs b/Casper.Network.SDK/Types/ValidatorBid.cs new file mode 100644 index 0000000..e0e9bfb --- /dev/null +++ b/Casper.Network.SDK/Types/ValidatorBid.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; +using System.Numerics; + +namespace Casper.Network.SDK.Types +{ + public class ValidatorBid + { + /// + /// The purse that was used for bonding. + /// + [JsonPropertyName("bonding_purse")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public URef BondingPurse { get; init; } + + /// + /// The delegation rate. + /// + /// + [JsonPropertyName("delegation_rate")] + public uint DelegationRate { get; init; } + + /// + /// `true` if validator has been "evicted" + /// + [JsonPropertyName("inactive")] + public bool Inactive { get; init; } + + /// + /// The amount of tokens staked by a validator (not including delegators). + /// + [JsonPropertyName("staked_amount")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger StakedAmount { get; init; } + + /// + /// Validator public key. + /// + [JsonPropertyName("validator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey PublicKey { get; init; } + + /// + /// Minimum allowed delegation amount in motes + /// + [JsonPropertyName("minimum_delegation_amount")] + public ulong MinimumDelegationAmount { get; init; } + + /// + /// Maximum allowed delegation amount in motes + /// + [JsonPropertyName("maximum_delegation_amount")] + public ulong MaximumDelegationAmount { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/ValidatorWeight.cs b/Casper.Network.SDK/Types/ValidatorWeight.cs index 1c9474d..7ba8191 100644 --- a/Casper.Network.SDK/Types/ValidatorWeight.cs +++ b/Casper.Network.SDK/Types/ValidatorWeight.cs @@ -6,7 +6,7 @@ namespace Casper.Network.SDK.Types { /// - /// A validator's weight. + /// A validator's public key paired with its weight, i.e. the total number of motes staked by it and its delegators. /// public class ValidatorWeight { diff --git a/Casper.Network.SDK/Utils/CEP57Checksum.cs b/Casper.Network.SDK/Utils/CEP57Checksum.cs index 51b6625..78f0f8c 100644 --- a/Casper.Network.SDK/Utils/CEP57Checksum.cs +++ b/Casper.Network.SDK/Utils/CEP57Checksum.cs @@ -134,27 +134,5 @@ public static byte[] Decode(string hex, out int checksumResult) return bytes; } - - public class HashWithChecksumConverter : JsonConverter - { - public override string Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - var hex = reader.GetString(); - CEP57Checksum.Decode(hex, out var checksumResult); - if (checksumResult == CEP57Checksum.InvalidChecksum) - throw new JsonException("Wrong checksum in hexadecimal string."); - - return hex; - } - - public override void Write( - Utf8JsonWriter writer, - string hash, - JsonSerializerOptions options) => - writer.WriteStringValue(hash); - } } } \ No newline at end of file From 2a0555058fe7bded356e51579d1b01718d69bbd0 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 8 Jul 2024 18:27:48 +0200 Subject: [PATCH 053/126] WIP migration guide Signed-off-by: David Hernando --- Docs/Articles/CondorMigrationGuide.md | 118 ++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 Docs/Articles/CondorMigrationGuide.md diff --git a/Docs/Articles/CondorMigrationGuide.md b/Docs/Articles/CondorMigrationGuide.md new file mode 100644 index 0000000..53810d5 --- /dev/null +++ b/Docs/Articles/CondorMigrationGuide.md @@ -0,0 +1,118 @@ +# Migration from Casper .NET SDK v2.x to v3.0 + +To migrate your application from Casper .NET SDK v2.x to Casper .NET SDK v3.0 you'll need to make several +changes on your code. The good news is that with version 3 of the SDK your application will be compatible with +Casper v1.5.6 as well as with Casper v2.0 (aka Condor). + +## Types + +### Blocks + +With Condor, block records use a new format to accommodate more types of Transactions and surface previously embedded +information. New blocks will use the new format; historical blocks retain their original format. + +In the SDK, this mean you can deal with two versions of blocks. `BlockV1` are blocks produced before Condor, i.e. within +Casper 1.x protocol version. `BlockV2` are blocks produced after Condor upgrade. + +To facilitate handling different versions, the SDK implements the type `Block` which can be either a V1 or V2 type in +the network. This type contains all the data you may want to query: + +```csharp +public class Block +{ + public int Version { get; init; } + public string Hash { get; init; } + public string AccumulatedSeed { get; init; } + public ulong EraId { get; init; } + public ulong Height { get; init; } + public string ParentHash { get; init; } + public string ProtocolVersion { get; init; } + public bool RandomBit { get; init; } + public string StateRootHash { get; init; } + public string Timestamp { get; init; } + public EraEnd EraEnd { get; init; } + public UInt16 CurrentGasPrice { get; init; } + public Proposer Proposer { get; init; } + public string LastSwitchBlockHash { get; init; } + public List Transactions { get; init; } + public List> RewardedSignatures { get; init; } +} +``` + +Note that `Block` does not have a header nor body parts. + +Also, be aware that some properties may be `null` if they're not part of the versioned block. For +example, `LastSwitchBlockHas` is present only for V2 blocks. + +#### Recovering the versioned block object + +If, for any reason, you need to work with the original format of the block, you can cast a `Block` to a `BlockV1` +or `BlockV2`. For example: + +```csharp +if (block.Version == 2) { + var blockv2 = (BlockV2)block; + // ... +} else if (blockVersion == 1) { + var blockv1 = (BlockV1)block; + // ... +} +``` + +#### EraEnd + +`EraEnd` structure also differs for switch blocks produced before and after Condor. In most cases you can just use +the `EraEnd` object from the common `Block` class. But again, if you need it, you can recover an `EraEndV1` object from +a V1 block: + +```csharp +if (block.Version == 1) { + var blockv1 = (BlockV1)block; + var eraEnd = blockv1.Header.EraEnd; + // ... +} +``` + +#### Transactions + +Blocks produced on Casper 1.x contain a list of deploys which differentiate between native transfers and all other types +of deploys. On Condor, transactions (this includes legacy deploys too) have a category field which determines the +processing lane the transaction is sent to. The chainspec of the network determines the properties of each lane: + +1. Maximum transaction size in bytes for a given transaction in a certain lane. +2. Maximum args length size in bytes for a given transaction in a certain lane. +3. Transaction gas limit size in motes for a given transaction in a certain lane. +4. The maximum number of transactions the lane can contain for one block. + +The categories (i.e. lanes) defined so far are: + +1. Mint (native CSPR transfers) transactions +2. Auction (native interaction with the Auction contract) transactions +3. Install/Upgrade transactions +4. Large transactions +5. Medium transactions +6. Small transactions + +A `Block` contains a list of transactions: + +```csharp +public List Transactions { get; init; } +``` + +For each transaction, the category, the Version (either a legacy `Deploy` or the new `TransactionV1`) and its `Hash` is +provided. + +`Deploy`s in V1 blocks are categorized as `Mint` for native transfer deploys and `Large` for all the rest. + +In this list, each transaction has + +#### Block height type + +The type to represent block height is now `ulong` everywhere. `int` was used in some methods or types in the previous +version. That's not the case with v3. + +## Other changes + +### Checksums (CEP-57) + +On SDK v3 only public keys are checksummed. The rest of keys and hashes are not checksummed anymore. From 9992e48619bd233156fc5bbd5e1b9e4874ca4a39 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 9 Jul 2024 12:08:37 +0200 Subject: [PATCH 054/126] WIP Signed-off-by: David Hernando --- Docs/Articles/CondorMigrationGuide.md | 106 ++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 6 deletions(-) diff --git a/Docs/Articles/CondorMigrationGuide.md b/Docs/Articles/CondorMigrationGuide.md index 53810d5..16a7312 100644 --- a/Docs/Articles/CondorMigrationGuide.md +++ b/Docs/Articles/CondorMigrationGuide.md @@ -4,9 +4,11 @@ To migrate your application from Casper .NET SDK v2.x to Casper .NET SDK v3.0 yo changes on your code. The good news is that with version 3 of the SDK your application will be compatible with Casper v1.5.6 as well as with Casper v2.0 (aka Condor). -## Types +This guide does not replace other Condor-related documents that introduce the changes in the new version of the Casper +network +software. We encourage the reader to get familiar with the new concepts first. -### Blocks +## Blocks With Condor, block records use a new format to accommodate more types of Transactions and surface previously embedded information. New blocks will use the new format; historical blocks retain their original format. @@ -44,7 +46,7 @@ Note that `Block` does not have a header nor body parts. Also, be aware that some properties may be `null` if they're not part of the versioned block. For example, `LastSwitchBlockHas` is present only for V2 blocks. -#### Recovering the versioned block object +### Recovering the versioned block object If, for any reason, you need to work with the original format of the block, you can cast a `Block` to a `BlockV1` or `BlockV2`. For example: @@ -59,7 +61,7 @@ if (block.Version == 2) { } ``` -#### EraEnd +### EraEnd `EraEnd` structure also differs for switch blocks produced before and after Condor. In most cases you can just use the `EraEnd` object from the common `Block` class. But again, if you need it, you can recover an `EraEndV1` object from @@ -73,7 +75,7 @@ if (block.Version == 1) { } ``` -#### Transactions +### Transactions included in a block Blocks produced on Casper 1.x contain a list of deploys which differentiate between native transfers and all other types of deploys. On Condor, transactions (this includes legacy deploys too) have a category field which determines the @@ -106,11 +108,103 @@ provided. In this list, each transaction has -#### Block height type +### Block height type The type to represent block height is now `ulong` everywhere. `int` was used in some methods or types in the previous version. That's not the case with v3. +## Account/Contract Merge + +On Condor, accounts and contracts are stored with the new type `AddressableEntity`. The `EntityKind` property in +this type permits to know whether the record is an `Account`, a stored `SmartContract` or a `System` contract. + +### GetEntity RPC method + +Use the new method`GetEntity(IEntityIdentifier)` in the RPC interface to retrieve a +record. `PublicKey`, `AccountHashKey` +and `AddressableEntityKey` implement the `IEntityIdentiifer` interface and can be used to retrieve +and `AddressableEntity` from the network. While `PublicKey` and `AccountHashKey`are known, the `AddressableEntitykey` is +new but the developer must come familiar with it to work with Condor. Some examples of this key are: + +``` +entity-account-2f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c458007309 +entity-contract-a5cf5917505ef60a6f0df395dd19e86a0f075d00f2e6ce49f5aa0e18f6e26f5d +entity-system-a1b5f200a58533875ef83cb98de14f128342b34162cbc14d4f41f3ccbc451dc3 +``` + +### Legacy accounts + +Existing accounts are migrated either during the Condor upgrade or when they interact with the network for +the first time after the upgrade. The exact time depends on the `chainspec` agreed for the Condor upgrade. Hash values +for the `AccountHashKey` and the `EntityKey` are shared and remain unchanged for migrated accounts. + +Non-migrated accounts can be retrieved also with the new `GetEntity` method. In this case, the response contains the +account info in the `LegacyAccount` property instead of in the `Entity` property. + +Alternatively, the legacy method `GetAccountInfo` works also for non-migrated accounts. + +### Contract information + +Similar to accounts, contract records are migrated also to the new `AddressableEntity`. After the migration, +only `GetEntity` method can be used to retrieve contract information. Hash values for contracts and packages remain +unchaged for the migrated records. + +To retrieve information about the contract package use the `QueryGlobalState` method with the `PackageKey` of the +contract. + +### Backwards compatibility + +When using the SDK v3 with a Casper v1.x network, only `GetAccountInfo` can be used to retrieve account information. For +contract and package information use `QueryGlobalState`. + +## Balances + +The new `NoFee` mode in Condor introduces the concept of a 'balance hold'. In this mode of operation, for each +transaction +the network holds the amount paid for its execution in the paying purse. Thus, entities have a total balance and an +available balance, where available balance is equal to the total balance minus the balance holds. + +To get a detailed information of an entity balance use the new method `QueryBalanceDetails(IPurseIdentifier)` with a +purse identifier. `PublicKey`, `AccountHashKey`, `AddressableEntityKey` and `URef` keys implement the `IPurseIdentifier` +interface and +can be used to retrieve the balance details for an entity. + +`GetAccountBalance` has been renamed to `GetBalance` since this method can be used for any type of entity, not only for +accounts. + +## Deploys and Transactions + +Condor introduces a new transaction model to support advanced use cases. While `Deploy`s continue to work in Condor, +this type is deprecated and Casper recommends to switch to the new `TransactionV1` type. + +Similar to the `DeployTemplates` class which provided deploy templates for most common use cases, we plan to implement +a `TransactionV1Templates` class for the new model of transactions in a next release of this SDK. + +Use the new method `PutTransaction` to send a `TransactionV1` to the network. To retrieve an accepted transaction use +the new `GetTransaction` method. `GetTransaction` can be used also to retrieve a `Deploy`. For non processed +transactions the `ExecutionInfo` in the response is null. Upon processing, this property contains all information about +the execution, including cost, payments, errors (if any) and execution effects. + +### Payments and costs + +For a transaction (old and new types) processed in Condor, the execution results object contain three properties related +to the gas consumed and the CSPR tokens paid: + +- `limit`: The maximum allowed gas limit for the transaction. +- `consumed`: How much gas was consumed executing the transaction. +- `cost`: How much CSPR was paid/held for the transaction. + +In the `NoFee` model, the user does not specify any amount for payment. Instead, he must use the `Fixed` pricing mode +with a gas price tolerance. The network chainspec defines the gas limit for each of the transaction categories (see +'Transactions included in a block' section above). Then for each era the network determines a gas price based on the +previous era load. This price works as a multiplier for the consumed gas and relates to the gas price +tolerance specified in the transaction. If the tolerance is lower than the gas price, the transaction won't be +processed. The consumed gas in a transaction must be always lower than the limit or it will fail with an out of gas +error. Finally, the cost in the no-fee model is the limit multiplied by the gas price. + +### Execution results + + ## Other changes ### Checksums (CEP-57) From 60d7fb92d9db886c6d8f78d8cd209610e7caccea Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 9 Jul 2024 12:47:37 +0200 Subject: [PATCH 055/126] WIP Signed-off-by: David Hernando --- Docs/Articles/CondorMigrationGuide.md | 30 ++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/Docs/Articles/CondorMigrationGuide.md b/Docs/Articles/CondorMigrationGuide.md index 16a7312..f72ce91 100644 --- a/Docs/Articles/CondorMigrationGuide.md +++ b/Docs/Articles/CondorMigrationGuide.md @@ -55,7 +55,7 @@ or `BlockV2`. For example: if (block.Version == 2) { var blockv2 = (BlockV2)block; // ... -} else if (blockVersion == 1) { +} else if (block.Version == 1) { var blockv1 = (BlockV1)block; // ... } @@ -204,9 +204,37 @@ error. Finally, the cost in the no-fee model is the limit multiplied by the gas ### Execution results +The `ExecutionResult` class is a versioned object. Deploys processed before Condor upgrade are stored in the global +state as `ExecutionResultV1`records. And deploys and transactions processed after Condor upgrade are stored +as `ExecutionResultV2` records. + +The `GetTransaction` method always returns a `ExecutionResult` instance regardless the version. It has the same fields +than `ExecutionResultV2`. The user can obtain the original structure with casting this instance to the correct version: + +```csharp +if (executionResult.Version == 2) { + var executionResultV2 = (ExecutionResultV2)executionResult; + // ... +} else if (executionResult.Version == 1) { + var executionResultV1 = (ExecutionResultV1)executionResult; + // ... +} +``` + +The `Effect` property contains a list of `Transforms` that modify the global state. Note that the `ExecutionEffect` +which contained also a list of operations in addition to the transforms has been removed in Condor execution results. ## Other changes +### Last switch block hash + +For a Condor network, it is possible to get the latest switch block hash with the `GetNodeStatus` method. The response +contains the `LatestSwitchBlockHash` property with this value. + +Also, for blocks produced in Casper 2.0, the `Block` instance contains the previous switch block hash in +the `LastSwitchBlockHash` property. + ### Checksums (CEP-57) On SDK v3 only public keys are checksummed. The rest of keys and hashes are not checksummed anymore. + From 5c6ab921cb5e6889ab2df9dfc80f7de05c4fc23f Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 9 Jul 2024 16:42:21 +0200 Subject: [PATCH 056/126] WIP migration guide Signed-off-by: David Hernando --- Docs/Articles/CondorMigrationGuide.md | 43 +++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/Docs/Articles/CondorMigrationGuide.md b/Docs/Articles/CondorMigrationGuide.md index f72ce91..ec35bb0 100644 --- a/Docs/Articles/CondorMigrationGuide.md +++ b/Docs/Articles/CondorMigrationGuide.md @@ -224,6 +224,49 @@ if (executionResult.Version == 2) { The `Effect` property contains a list of `Transforms` that modify the global state. Note that the `ExecutionEffect` which contained also a list of operations in addition to the transforms has been removed in Condor execution results. +## Auction contract + +The auction contract also has changed in Condor. If your application tracks validator bids, rewards and delegators, +you'll need to rework the way network responses are parsed and interpreted. A complete description of the changes cannot +covered in this guide. + +## Server Sent Events + +The `ServerEventsClient` class can listen to both an event stream from a `v1.x` node as well as from `v2.x`. + +### Blocks + +`BlockedAdded` event contains a `Block` object. When needed, the original block structure `BlockV1` or `BlockV2` can be +obtained with cast operator as described above. + +### Transactions + +On Condor, the events `DeployAccepted`, `DeployProcessed` and `DeployExpired` are replaced with the +equivalent `TransactionAccepted`, `TransactionProcessed` and `TransactionExpired`. These new events are emitted for +both `Deploy` and `TransactionV1` types of transactions. + +A `TransactionAccepted` event contains a `Transaction` property with either a `Deploy` or a `TransactionV1`. + +`TransactionProcessed` and `TransactionExpired` events contain a `TransactionHash` property with either a deploy hash or +a transaction version 1 hash. + +### Finality signatures + +The `FinalitySignature` event contains an instance of the versioned `FinalitySignature` class. Version 2 of this type is +an extension that contains all properties in version 1 plus the block height and the chain name hash. + +The user can obtain the original structure with casting this instance to the correct version: + +```csharp +if (finalitySignature.Version == 2) { + var finalitySignatureV2 = (FinalitySignatureV2)finalitySignature; + // ... +} else if (finalitySignature.Version == 1) { + var finalitySignatureV1 = (FinalitySignatureV1)finalitySignature; + // ... +} +``` + ## Other changes ### Last switch block hash From a8c2c6bc27cb5ea9d723e9b689a24d5a9ae49358 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 9 Jul 2024 17:23:42 +0200 Subject: [PATCH 057/126] Corrections. WIP. Signed-off-by: David Hernando --- Docs/Articles/CondorMigrationGuide.md | 152 ++++++++------------------ 1 file changed, 44 insertions(+), 108 deletions(-) diff --git a/Docs/Articles/CondorMigrationGuide.md b/Docs/Articles/CondorMigrationGuide.md index ec35bb0..a86cc2b 100644 --- a/Docs/Articles/CondorMigrationGuide.md +++ b/Docs/Articles/CondorMigrationGuide.md @@ -1,23 +1,14 @@ # Migration from Casper .NET SDK v2.x to v3.0 -To migrate your application from Casper .NET SDK v2.x to Casper .NET SDK v3.0 you'll need to make several -changes on your code. The good news is that with version 3 of the SDK your application will be compatible with -Casper v1.5.6 as well as with Casper v2.0 (aka Condor). +To migrate your application from Casper .NET SDK v2.x to Casper .NET SDK v3.0, you’ll need to make several changes to your code. The good news is that version 3 of this SDK keeps compatibility with Casper v1.5.6 nodes; therefore, once you update your application, it will work before and after the Condor upgrade. -This guide does not replace other Condor-related documents that introduce the changes in the new version of the Casper -network -software. We encourage the reader to get familiar with the new concepts first. +This guide outlines the changes necessary for your application. However, it's worth noting that it doesn't replace other Condor-related documents introducing the new concepts in Casper 2.0 or migration guides. We strongly advise the reader to understand these new concepts first, as they will significantly aid in grasping the changes. ## Blocks -With Condor, block records use a new format to accommodate more types of Transactions and surface previously embedded -information. New blocks will use the new format; historical blocks retain their original format. +With Condor, produced blocks are stored in a new format that extends the information contained compared to old blocks. Blocks produced before the upgrade keep their original format. Thus, the SDK implements `BlockV1` and `BlocV2` classes to handle old and new block formats, respectively. -In the SDK, this mean you can deal with two versions of blocks. `BlockV1` are blocks produced before Condor, i.e. within -Casper 1.x protocol version. `BlockV2` are blocks produced after Condor upgrade. - -To facilitate handling different versions, the SDK implements the type `Block` which can be either a V1 or V2 type in -the network. This type contains all the data you may want to query: +To facilitate handling different versions, the SDK also implements the type `Block`, which can represent either a V1 or V2 type in the network. This is the type obtained by default in the RPC queries and the SSE channel and contains all the data you may want to query: ```csharp public class Block @@ -41,15 +32,13 @@ public class Block } ``` -Note that `Block` does not have a header nor body parts. +Note that `Block` does not have a header or body parts. -Also, be aware that some properties may be `null` if they're not part of the versioned block. For -example, `LastSwitchBlockHas` is present only for V2 blocks. +Also, some properties may have a `null` value if they’re not part of the versioned block. For example, `LastSwitchBlockHas` is present only for V2 blocks and `null` for V1 blocks. ### Recovering the versioned block object -If, for any reason, you need to work with the original format of the block, you can cast a `Block` to a `BlockV1` -or `BlockV2`. For example: +If, for any reason, you need to work with the original format of the block, you can cast a Block into BlockV1 or BlockV2. For example: ```csharp if (block.Version == 2) { @@ -63,9 +52,7 @@ if (block.Version == 2) { ### EraEnd -`EraEnd` structure also differs for switch blocks produced before and after Condor. In most cases you can just use -the `EraEnd` object from the common `Block` class. But again, if you need it, you can recover an `EraEndV1` object from -a V1 block: +EraEnd structure also differs for switch blocks produced before and after Condor. In most cases, you can just use the EraEnd object from the common Block class. But again, if necessary, you can recover an EraEndV1 object from a Version 1 Block instance: ```csharp if (block.Version == 1) { @@ -77,16 +64,14 @@ if (block.Version == 1) { ### Transactions included in a block -Blocks produced on Casper 1.x contain a list of deploys which differentiate between native transfers and all other types -of deploys. On Condor, transactions (this includes legacy deploys too) have a category field which determines the -processing lane the transaction is sent to. The chainspec of the network determines the properties of each lane: +Blocks produced on Casper v1.x contain a list of deploys that differentiate between native transfers and all other types of deploys. On Condor, transactions (this includes legacy deploys too) have a category field that determines the processing lane to which the transaction is sent. The chainspec of the network determines the properties of each lane: 1. Maximum transaction size in bytes for a given transaction in a certain lane. 2. Maximum args length size in bytes for a given transaction in a certain lane. 3. Transaction gas limit size in motes for a given transaction in a certain lane. 4. The maximum number of transactions the lane can contain for one block. -The categories (i.e. lanes) defined so far are: +The categories (i.e., lanes) defined so far are: 1. Mint (native CSPR transfers) transactions 2. Auction (native interaction with the Auction contract) transactions @@ -101,30 +86,22 @@ A `Block` contains a list of transactions: public List Transactions { get; init; } ``` -For each transaction, the category, the Version (either a legacy `Deploy` or the new `TransactionV1`) and its `Hash` is -provided. - -`Deploy`s in V1 blocks are categorized as `Mint` for native transfer deploys and `Large` for all the rest. +The category, version (either a legacy Deploy or the new TransactionV1 type), and the hash are provided for each transaction. -In this list, each transaction has +`Deploy`s in V1 blocks are categorized as `Mint` for native transfer deploys and `Large` for all the rest. The same happens for legacy deploys sent to a Casper v2.x node. ### Block height type The type to represent block height is now `ulong` everywhere. `int` was used in some methods or types in the previous -version. That's not the case with v3. +version. That's not the case with Casper .NET SDK v3. ## Account/Contract Merge -On Condor, accounts and contracts are stored with the new type `AddressableEntity`. The `EntityKind` property in -this type permits to know whether the record is an `Account`, a stored `SmartContract` or a `System` contract. +On Condor, accounts and contracts are stored with the new type AddressableEntity. The EntityKind property in this type permits knowing whether the record is an Account, a stored SmartContract, or a System contract. ### GetEntity RPC method -Use the new method`GetEntity(IEntityIdentifier)` in the RPC interface to retrieve a -record. `PublicKey`, `AccountHashKey` -and `AddressableEntityKey` implement the `IEntityIdentiifer` interface and can be used to retrieve -and `AddressableEntity` from the network. While `PublicKey` and `AccountHashKey`are known, the `AddressableEntitykey` is -new but the developer must come familiar with it to work with Condor. Some examples of this key are: +Use the new method GetEntity(IEntityIdentifier) in the RPC interface to retrieve a record. PublicKey, AccountHashKey, and AddressableEntityKey implement the IEntityIdentiifer interface and can be used to retrieve an AddressableEntity from the network. While PublicKey and AccountHashKeyare known types from Casper v1.x, the AddressableEntitykey is new, but the developer must become familiar with it to work with Condor. Some examples of this key are: ``` entity-account-2f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c458007309 @@ -134,82 +111,53 @@ entity-system-a1b5f200a58533875ef83cb98de14f128342b34162cbc14d4f41f3ccbc451dc3 ### Legacy accounts -Existing accounts are migrated either during the Condor upgrade or when they interact with the network for -the first time after the upgrade. The exact time depends on the `chainspec` agreed for the Condor upgrade. Hash values -for the `AccountHashKey` and the `EntityKey` are shared and remain unchanged for migrated accounts. +Existing accounts are migrated either during the Condor upgrade or when they interact with the network for the first time after the upgrade. The exact time depends on the `chainspec` agreed for the Condor upgrade. Hash values for the `AccountHashKey` and the `EntityKey` are shared and remain unchanged for migrated accounts. -Non-migrated accounts can be retrieved also with the new `GetEntity` method. In this case, the response contains the -account info in the `LegacyAccount` property instead of in the `Entity` property. +Non-migrated accounts can also be retrieved using the new `GetEntity` method. In this case, the response contains the account info in the `LegacyAccount` property instead of the `Entity` property. -Alternatively, the legacy method `GetAccountInfo` works also for non-migrated accounts. +Alternatively, the legacy method `GetAccountInfo` also works for non-migrated accounts. ### Contract information -Similar to accounts, contract records are migrated also to the new `AddressableEntity`. After the migration, -only `GetEntity` method can be used to retrieve contract information. Hash values for contracts and packages remain -unchaged for the migrated records. +Like accounts, contract records have also been migrated to the new `AddressableEntity`. After the migration, only the `GetEntity` method can be used to retrieve contract information. Hash values for contracts and packages remain unchanged for the migrated records. -To retrieve information about the contract package use the `QueryGlobalState` method with the `PackageKey` of the -contract. +To retrieve information about the contract package, use the `QueryGlobalState` method with the `PackageKey` of the contract. ### Backwards compatibility -When using the SDK v3 with a Casper v1.x network, only `GetAccountInfo` can be used to retrieve account information. For -contract and package information use `QueryGlobalState`. +When using the SDK v3 with a Casper v1.x network, only GetAccountInfo can be used to retrieve account information. For contract and package information use QueryGlobalState. ## Balances -The new `NoFee` mode in Condor introduces the concept of a 'balance hold'. In this mode of operation, for each -transaction -the network holds the amount paid for its execution in the paying purse. Thus, entities have a total balance and an -available balance, where available balance is equal to the total balance minus the balance holds. +The new `NoFee` mode in Condor introduces the concept of a ‘balance hold’. In this mode of operation, for each transaction, the network holds the amount paid for its execution in the paying purse. Thus, entities have a total balance and an available balance, where the available balance is equal to the total balance minus the balance holds. -To get a detailed information of an entity balance use the new method `QueryBalanceDetails(IPurseIdentifier)` with a -purse identifier. `PublicKey`, `AccountHashKey`, `AddressableEntityKey` and `URef` keys implement the `IPurseIdentifier` -interface and -can be used to retrieve the balance details for an entity. +To get detailed information on an entity balance, use the new method `QueryBalanceDetails(IPurseIdentifier)` with a purse identifier. `PublicKey`, `AccountHashKey`, `AddressableEntityKey`, and `URef` keys implement the `IPurseIdentifier` interface and can be used to retrieve the balance details for an entity. -`GetAccountBalance` has been renamed to `GetBalance` since this method can be used for any type of entity, not only for -accounts. +`GetAccountBalance` has been renamed to `GetBalance` since this method can be used for any type of entity, not only for accounts. ## Deploys and Transactions -Condor introduces a new transaction model to support advanced use cases. While `Deploy`s continue to work in Condor, -this type is deprecated and Casper recommends to switch to the new `TransactionV1` type. +Condor introduces a new transaction model to support advanced use cases. While `Deploy`s continue to work in Condor, this type is deprecated, and Casper recommends switching to the new `TransactionV1` type. -Similar to the `DeployTemplates` class which provided deploy templates for most common use cases, we plan to implement -a `TransactionV1Templates` class for the new model of transactions in a next release of this SDK. +Similar to the `DeployTemplates` class, which provides deploy templates for most common use cases, we plan to implement a TransactionV1Templates class for the new transaction model in the next release of this SDK. -Use the new method `PutTransaction` to send a `TransactionV1` to the network. To retrieve an accepted transaction use -the new `GetTransaction` method. `GetTransaction` can be used also to retrieve a `Deploy`. For non processed -transactions the `ExecutionInfo` in the response is null. Upon processing, this property contains all information about -the execution, including cost, payments, errors (if any) and execution effects. +Use the new method `PutTransaction` to send a `TransactionV1` to the network. Use the new `GetTransaction` method to retrieve an accepted transaction. Both methods can also be used to send and retrieve a Deploy. For unprocessed transactions, the ExecutionInfo in the response is `null`. Upon processing, this property contains all information about the execution, including cost, payments, errors (if any), and execution effects. ### Payments and costs -For a transaction (old and new types) processed in Condor, the execution results object contain three properties related -to the gas consumed and the CSPR tokens paid: +For a transaction (old and new versions) processed in Condor, the execution results object contains three properties related to the gas consumed and the CSPR tokens paid: - `limit`: The maximum allowed gas limit for the transaction. -- `consumed`: How much gas was consumed executing the transaction. -- `cost`: How much CSPR was paid/held for the transaction. +- `consumed`: Gas consumed executing the transaction. +- `cost`: CSPR paid/held for the transaction. -In the `NoFee` model, the user does not specify any amount for payment. Instead, he must use the `Fixed` pricing mode -with a gas price tolerance. The network chainspec defines the gas limit for each of the transaction categories (see -'Transactions included in a block' section above). Then for each era the network determines a gas price based on the -previous era load. This price works as a multiplier for the consumed gas and relates to the gas price -tolerance specified in the transaction. If the tolerance is lower than the gas price, the transaction won't be -processed. The consumed gas in a transaction must be always lower than the limit or it will fail with an out of gas -error. Finally, the cost in the no-fee model is the limit multiplied by the gas price. +In the NoFee model, the user does not specify any amount for payment. Instead, he must use the Fixed pricing mode with a gas price tolerance. The network chainspec defines the gas limit for each transaction category (see the ‘Transactions included in a block’ section above). Then, the network determines a gas price for each era based on the previous era's load. This price works as a multiplier for the consumed gas and relates to the gas price tolerance specified in the transaction. The transaction won't be processed if the tolerance is lower than the gas price. The gas consumed in a transaction must always be lower than the limit, or it will fail with an out-of-gas error. Finally, the cost in the no-fee model is the limit multiplied by the gas price. ### Execution results -The `ExecutionResult` class is a versioned object. Deploys processed before Condor upgrade are stored in the global -state as `ExecutionResultV1`records. And deploys and transactions processed after Condor upgrade are stored -as `ExecutionResultV2` records. +The `ExecutionResult` class is a versioned object. Deploys processed before the Condor upgrade are stored in the global state as `ExecutionResultV1`records, and deploys and transactions processed after the Condor upgrade are stored as `ExecutionResultV2` records. -The `GetTransaction` method always returns a `ExecutionResult` instance regardless the version. It has the same fields -than `ExecutionResultV2`. The user can obtain the original structure with casting this instance to the correct version: +For a processed transaction, the `GetTransaction` method always returns an `ExecutionResult` instance regardless of the version. It has the same fields as `ExecutionResultV2`. The user can obtain the original structure by casting this instance to the correct version: ```csharp if (executionResult.Version == 2) { @@ -221,41 +169,31 @@ if (executionResult.Version == 2) { } ``` -The `Effect` property contains a list of `Transforms` that modify the global state. Note that the `ExecutionEffect` -which contained also a list of operations in addition to the transforms has been removed in Condor execution results. +The Effect property contains a list of Transforms that modify the global state due to the transaction execution. Note that the ExecutionEffect type in Casper v1.x, which also contained a list of operations in addition to the transforms, has been removed in Condor execution results. ## Auction contract -The auction contract also has changed in Condor. If your application tracks validator bids, rewards and delegators, -you'll need to rework the way network responses are parsed and interpreted. A complete description of the changes cannot -covered in this guide. +The auction contract also has changed in Condor. If your application tracks validator bids, rewards, and delegators, you must rework how network responses are parsed and interpreted. A complete description of the changes cannot covered in this guide. ## Server Sent Events -The `ServerEventsClient` class can listen to both an event stream from a `v1.x` node as well as from `v2.x`. +The ServerEventsClient class can listen to an event stream from a v1.x node and from v2.x. ### Blocks -`BlockedAdded` event contains a `Block` object. When needed, the original block structure `BlockV1` or `BlockV2` can be -obtained with cast operator as described above. +The BlockedAdded event contains a Block object. The original block structure, BlockV1 or BlockV2, can be obtained with the cast operator, as described above. ### Transactions -On Condor, the events `DeployAccepted`, `DeployProcessed` and `DeployExpired` are replaced with the -equivalent `TransactionAccepted`, `TransactionProcessed` and `TransactionExpired`. These new events are emitted for -both `Deploy` and `TransactionV1` types of transactions. - -A `TransactionAccepted` event contains a `Transaction` property with either a `Deploy` or a `TransactionV1`. +On Condor, the `DeployAccepted`, `DeployProcessed`, and `DeployExpired` events are replaced with the equivalent `TransactionAccepted`, `TransactionProcessed`, and `TransactionExpired` events. These are emitted for both `Deploy` and `TransactionV1` types of transactions. -`TransactionProcessed` and `TransactionExpired` events contain a `TransactionHash` property with either a deploy hash or -a transaction version 1 hash. +A `TransactionAccepted` event contains a `Transaction` property with either a `Deploy` or a `TransactionV1`. `TransactionProcessed` and `TransactionExpired` events contain a `TransactionHash` property with either a deploy hash or a transaction version 1 hash. ### Finality signatures -The `FinalitySignature` event contains an instance of the versioned `FinalitySignature` class. Version 2 of this type is -an extension that contains all properties in version 1 plus the block height and the chain name hash. +The `FinalitySignature` event contains an instance of the versioned `FinalitySignature` class. Version 2 of this type is an extension that contains all properties in version 1 plus the block height and the chain name hash. -The user can obtain the original structure with casting this instance to the correct version: +The user can obtain the original structure by casting this instance to the correct version: ```csharp if (finalitySignature.Version == 2) { @@ -271,13 +209,11 @@ if (finalitySignature.Version == 2) { ### Last switch block hash -For a Condor network, it is possible to get the latest switch block hash with the `GetNodeStatus` method. The response -contains the `LatestSwitchBlockHash` property with this value. +For a Condor network, the `GetNodeStatus` method can be used to get the latest switch block hash. The response contains the `LatestSwitchBlockHash` property with this value. -Also, for blocks produced in Casper 2.0, the `Block` instance contains the previous switch block hash in -the `LastSwitchBlockHash` property. +Also, for blocks produced in Casper 2.0, the `Block` instance contains the previous switch block hash in the `LastSwitchBlockHash` property. ### Checksums (CEP-57) -On SDK v3 only public keys are checksummed. The rest of keys and hashes are not checksummed anymore. +On SDK v3 only public keys are checksummed with the CEP-57 standard. The rest of the keys and hashes are not checksummed anymore. From 205f95c84e5d2403b184f8263c346e524f226c85 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 10 Jul 2024 10:26:55 +0200 Subject: [PATCH 058/126] Added info about versioned Transfer class to migration guide. Signed-off-by: David Hernando --- Docs/Articles/CondorMigrationGuide.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Docs/Articles/CondorMigrationGuide.md b/Docs/Articles/CondorMigrationGuide.md index a86cc2b..e2a904e 100644 --- a/Docs/Articles/CondorMigrationGuide.md +++ b/Docs/Articles/CondorMigrationGuide.md @@ -95,6 +95,24 @@ The category, version (either a legacy Deploy or the new TransactionV1 type), an The type to represent block height is now `ulong` everywhere. `int` was used in some methods or types in the previous version. That's not the case with Casper .NET SDK v3. +## Transfers + +The response in `GetBlockTransfers()` method contains a list of `Transfer` objects. This is also a versioned object. It contains all the information related to the information. + +If you need to recover the original format, cast an instance to a `TransferV1` or `TransferV2` object: + +```csharp +var response = await rpcClient.GetBlockTransfers(); +var result = response.Parse(); +foreach(var transfer in result.Transfers) +{ + if (transfer.Version == 1) { + var transferv1 = (TransferV1)transfer; + // ... + } +} +``` + ## Account/Contract Merge On Condor, accounts and contracts are stored with the new type AddressableEntity. The EntityKind property in this type permits knowing whether the record is an Account, a stored SmartContract, or a System contract. From 8ce1bba93c5908b30e52513e47741fd188faafb9 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 10 Jul 2024 12:04:42 +0200 Subject: [PATCH 059/126] Added changelog for v3.0.0-beta1. Added info about changes in GetDeploy() response. Signed-off-by: David Hernando --- CHANGELOG.md | 49 +++++++++++++++++++++++++++ Docs/Articles/CondorMigrationGuide.md | 7 +++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 190db34..1426f68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,54 @@ All notable changes to this project will be documented in this file. The format [comment]: <> (Fixed: any bug fixes) [comment]: <> (Security: in case of vulnerabilities) +## [3.0.0-beta1] + +This version is compatible with Casper node v2.0.0-rc3 and Casper node v1.5.6. + +### Added + +* New `GetEntity()` method added to the RPC client. +* New `QueryBalanceDetails()` method added to the RPC client. +* New `PutTransaction()` and `GetTransaction()` methods added to the RPC client. +* New `TransactionV1` class to model the new type of transactions in Condor. +* New global state keys added to the SDK: `AddressableEntityKey`, `BalanceHoldKey`, `BidAddrKey`, `BlockGlobalAddrKey`, `ByteCodeKey`, `EntryPointKey`, `MessageKey`, `NamedKeyKey` and `PackageKey`. +* New `AddressableEntity` class added. It may represent an account, a stored smart contract, or a system smart contract. +* New properties in the `StoredValue` class that can be retrieved with `QueryGlobalState()` method: `BidKind`, `Unbonding`, `AddressableEntity`, `Package`, `ByteCode`, `MessageTopicSummary`, `Message`, `NamedKey`, `Reservation`, and `EntryPoint`. +* New classes to represent data from the global state: `BidKind`, `UnbondingPurse`, `Package` and `ByteCode`. +* New `Message` class to contain data for native events included in the `TransactionAccepted` event from the SSE channel. +* Added `TransactionAccepted`, `TransactionProcessed`, and `TransactionExpired` events to the list of events emitted by a node through the SSE interface. + +### Changed + +* The `Block` class has changed to abstract the developer from the versioned block records returned from the network in Condor version. Refer to the migration guide for more information. +* For blocks produced in Casper 2.0, the `Block` instance contains the previous switch block hash in the `LastSwitchBlockHash` property. +* Ther `EraEnd` class has changed to abstract the developer from the versioned records returned from the network. More info in the migration guide. +* The `Transfer` class contained in the response for a `GetBlockTransfers()` call or in a `ExecutionResult` object has changed to abstract from the versioned transfer records returned from the network. Refer to the migration guide for more information. +* The input argument for the `QueryBalance()` method in the RPC client is any object from a class implementing the `IPurseIdentifier` interface. `PublicKey`, `AccountHashKey`, `URef`, and `AddressableEntity` classes implement this interface. +* The type to refer to block heights is now `ulong` across all the SDK. In previous version there was a mix of `ulong` and `int`. +* When using the `GetNodeStatus()` method with a Casper 2.0 node, the response contains the hash for the latest switch block in the `LatestSwitchBlockHash` property. +* `GetDeploy()` response has changed and now contains a `ExecutionInfo`object when the deploy has been processed instead a list of `ExecutionResult` objects. The execution info itself contains block information and a result object. +* Starting with this version of the SDK, only public keys are checksummed with the CEP-57 standard. The rest of the keys and hashes are not checksummed anymore. +* In the `StoredValue` class, `Transfer` has been renamed to `LegacyTransfer`. +* `DeployApproval` class has been renamed to `Approval` and is used for `Deploy` as well as for the new `TransactionV1` model. +* `ActionThresholds` class has now new `UpgradeManagement` property. +* The `EntryPoint` class has new entry point types and a new property `EntryPointPayment`. Both apply when working with Casper 2.0 only. +* `Step` event from SSE contains a list of `Transform` objects instead of a `ExecutionEffect` instance. +* `FinalitySignature` event contains `BlockHeight` and `ChainNameHash` value when connected to a Casper 2.0 node. +* `DeployProcessed` event from SSE contains a `ExecutionResultV1` object instead of a `ExecutionResult` object. + +### Deprecated + +* `Proposer.isSystem` property is marked as Obsolete. Use `IsSystem` with capital `I` instead. + +### Removed + +* `GetAccountBalance()` method in the RPC client has been removed. Use `QueryBalance()` instead. `GetBalance()` method exists to use `state_get_balance` from the RPC interface if needed. + +### Security + +* BouncyCastle package updated to 2.4.0 version. + ## [2.3.0] ### Added @@ -83,6 +131,7 @@ This new type permits to parse correctly the value `"00"` used for system blocks ### Added * Initial release of Casper .NET SDK. +[3.0.0-beta1]: https://github.com/make-software/casper-net-sdk/releases/tag/v3.0.0-beta1 [2.3.0]: https://github.com/make-software/casper-net-sdk/releases/tag/v2.3.0 [2.2.0]: https://github.com/make-software/casper-net-sdk/releases/tag/v2.2.0 [2.1.0]: https://github.com/make-software/casper-net-sdk/releases/tag/v2.1.0 diff --git a/Docs/Articles/CondorMigrationGuide.md b/Docs/Articles/CondorMigrationGuide.md index e2a904e..b136a39 100644 --- a/Docs/Articles/CondorMigrationGuide.md +++ b/Docs/Articles/CondorMigrationGuide.md @@ -97,7 +97,7 @@ version. That's not the case with Casper .NET SDK v3. ## Transfers -The response in `GetBlockTransfers()` method contains a list of `Transfer` objects. This is also a versioned object. It contains all the information related to the information. +The `Transfer` class contained in the response for a `GetBlockTransfers()` call or in a `ExecutionResult` object is also a versioned object. It contains all the information related to the transfer. If you need to recover the original format, cast an instance to a `TransferV1` or `TransferV2` object: @@ -161,6 +161,11 @@ Similar to the `DeployTemplates` class, which provides deploy templates for most Use the new method `PutTransaction` to send a `TransactionV1` to the network. Use the new `GetTransaction` method to retrieve an accepted transaction. Both methods can also be used to send and retrieve a Deploy. For unprocessed transactions, the ExecutionInfo in the response is `null`. Upon processing, this property contains all information about the execution, including cost, payments, errors (if any), and execution effects. + +### GetDeploy() + +Response from the `GetDeploy()` method has changed. Instead of a list of `ExecutionResult` objects, it now returns an instance of `ExecutionInfo` for a processed deploy. This instance contains block information and a results object. + ### Payments and costs For a transaction (old and new versions) processed in Condor, the execution results object contains three properties related to the gas consumed and the CSPR tokens paid: From e7f6378a119c7bf7f407525bc82e9b97e91baa3a Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 11 Jul 2024 14:36:59 +0200 Subject: [PATCH 060/126] added IsSuccess getter to ExecutionResult class Signed-off-by: David Hernando --- Casper.Network.SDK/Types/ExecutionResult.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Casper.Network.SDK/Types/ExecutionResult.cs b/Casper.Network.SDK/Types/ExecutionResult.cs index f00748c..3e8fefd 100644 --- a/Casper.Network.SDK/Types/ExecutionResult.cs +++ b/Casper.Network.SDK/Types/ExecutionResult.cs @@ -89,6 +89,15 @@ public static explicit operator ExecutionResult(ExecutionResultV1 executionResul [JsonPropertyName("error_message")] public string ErrorMessage { get; init; } + /// + /// True if the transaction was completed without error. + /// + [JsonIgnore] + public bool IsSuccess + { + get { return ErrorMessage == null; } + } + /// /// A record of transfers performed while executing this transaction. /// From 9872b0c3d6bb3e181094293462557a2eb688aa77 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 11 Jul 2024 19:52:24 +0200 Subject: [PATCH 061/126] Renamed LegacyTransfer to Transfer in StoredValue Fixed Transfer parsing on v156 network Fixed List parsing on v156 execution results (un/delegation) Updated integration tests for transfers Signed-off-by: David Hernando --- Casper.Network.SDK.Test/NctlTransferTest.cs | 23 +++--- .../QueryGlobalStateResultTest.cs | 30 +++++++ .../TestData/legacy_transfer_v200.json | 17 ++++ Casper.Network.SDK/Types/Delegator.cs | 78 ++++++++++++------- Casper.Network.SDK/Types/StoredValue.cs | 26 +++++-- Casper.Network.SDK/Types/Transfer.cs | 1 + 6 files changed, 132 insertions(+), 43 deletions(-) create mode 100644 Casper.Network.SDK.Test/RPCResponses/QueryGlobalStateResultTest.cs create mode 100644 Casper.Network.SDK.Test/TestData/legacy_transfer_v200.json diff --git a/Casper.Network.SDK.Test/NctlTransferTest.cs b/Casper.Network.SDK.Test/NctlTransferTest.cs index 9b231c9..f86e94e 100644 --- a/Casper.Network.SDK.Test/NctlTransferTest.cs +++ b/Casper.Network.SDK.Test/NctlTransferTest.cs @@ -50,13 +50,15 @@ public async Task WaitTransferExecutionTest() var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); var getResponse = await _client.GetDeploy(_transferDeployHash, tokenSource.Token); - var execResult = getResponse.Parse().ExecutionResults.First(); - Assert.IsTrue(execResult.IsSuccess); - + var execInfo = getResponse.Parse().ExecutionInfo; + var execResult = (ExecutionResultV1)execInfo.ExecutionResult; + Assert.IsTrue(execResult.ErrorMessage == null); + + var t = execResult.Transfers.First(); _transferKey = execResult.Transfers.First(); Assert.IsNotNull(_transferKey.ToString()); - _transferBlockHash = execResult.BlockHash; + _transferBlockHash = execInfo.BlockHash; Assert.IsNotNull(_transferBlockHash); } @@ -66,12 +68,13 @@ public async Task QueryTransferKey() Assert.IsNotNull(_transferKey, "This test must run after WaitTransferExecutionTest"); var getResponse = await _client.QueryGlobalState(_transferKey); + Console.WriteLine(getResponse.Result.GetRawText()); var transfer = getResponse.Parse().StoredValue.Transfer; Assert.AreEqual(_transferAmount, transfer.Amount); var getResponse2 = await _client.QueryGlobalStateWithBlockHash(_transferKey, _transferBlockHash); var transfer2 = getResponse.Parse().StoredValue.Transfer; - Assert.AreEqual(transfer.DeployHash, transfer2.DeployHash); + Assert.AreEqual(transfer.TransactionHash.Deploy, transfer2.TransactionHash.Deploy); } [Test, Order(4)] @@ -95,7 +98,7 @@ public async Task CatchFailedTransferTest() var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); var getResponse = await _client.GetDeploy(deploy.Hash, tokenSource.Token); - var execResult = getResponse.Parse().ExecutionResults.First(); + var execResult = getResponse.Parse().ExecutionInfo.ExecutionResult; Assert.IsFalse(execResult.IsSuccess); Assert.IsFalse(string.IsNullOrWhiteSpace(execResult.ErrorMessage)); } @@ -131,7 +134,7 @@ public async Task GetBlockTransfersTest() var response = await _client.GetBlockTransfers(_transferBlockHash); var result = response.Parse(); Assert.AreEqual(1, result.Transfers.Count); - Assert.AreEqual(_transferDeployHash, result.Transfers[0].DeployHash); + Assert.AreEqual(_transferDeployHash, result.Transfers[0].TransactionHash.Deploy); } [Test, Order(8)] @@ -157,8 +160,8 @@ public async Task DelegateTokens() var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); var getResponse = await _client.GetDeploy(deployHash, tokenSource.Token); - - var execResult = getResponse.Parse().ExecutionResults.First(); + Console.WriteLine(getResponse.Result.GetRawText()); + var execResult = getResponse.Parse().ExecutionInfo.ExecutionResult; Assert.IsTrue(execResult.IsSuccess); } @@ -186,7 +189,7 @@ public async Task UndelegateTokens() var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); var getResponse = await _client.GetDeploy(deployHash, tokenSource.Token); - var execResult = getResponse.Parse().ExecutionResults.First(); + var execResult = getResponse.Parse().ExecutionInfo.ExecutionResult; Assert.IsTrue(execResult.IsSuccess); } } diff --git a/Casper.Network.SDK.Test/RPCResponses/QueryGlobalStateResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/QueryGlobalStateResultTest.cs new file mode 100644 index 0000000..6addb97 --- /dev/null +++ b/Casper.Network.SDK.Test/RPCResponses/QueryGlobalStateResultTest.cs @@ -0,0 +1,30 @@ +using System.IO; +using System.Numerics; +using Casper.Network.SDK.JsonRpc.ResultTypes; +using NUnit.Framework; + +namespace NetCasperTest.RPCResponses +{ + public class QueryGlobalStateResultTest + { + [Test] + public void LegacyTransferTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/legacy_transfer_v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + Assert.IsNotNull(result.StoredValue.Transfer); + var transfer = result.StoredValue.Transfer; + Assert.AreEqual(new BigInteger(2500000000000), transfer.Amount); + Assert.AreEqual("cd91f138e82ddce5dfbb99c6bbf3f47caca439b81d7f43702cbebfa99bacbfd0", transfer.TransactionHash.Deploy); + Assert.AreEqual("account-hash-87516c22bca9a14179ebbbe646c8f911153fe53626126c1ba24293517c2e04a2", transfer.From.AccountHash.ToString()); + Assert.AreEqual("account-hash-1265caa7cd80f31f882ab6f5623d89741c77cf3a36a309b54ed55fdc0be227c9", transfer.To.ToString()); + Assert.AreEqual("uref-c003c853c6477137225914c7610cf2c30ed594480c1fccd6dfd58a633522edc6-007", transfer.Source.ToString()); + Assert.AreEqual("uref-c74ad99f2bca136c1e64b1a14bf576696363c25701d66d87adca6dbdb147b39d-004", transfer.Target.ToString()); + Assert.IsFalse(string.IsNullOrWhiteSpace(result.MerkleProof)); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/legacy_transfer_v200.json b/Casper.Network.SDK.Test/TestData/legacy_transfer_v200.json new file mode 100644 index 0000000..1e4fb86 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/legacy_transfer_v200.json @@ -0,0 +1,17 @@ +{ + "api_version": "2.0.0", + "block_header": null, + "stored_value": { + "LegacyTransfer": { + "deploy_hash": "cd91f138e82ddce5dfbb99c6bbf3f47caca439b81d7f43702cbebfa99bacbfd0", + "from": "account-hash-87516c22bca9a14179ebbbe646c8f911153fe53626126c1ba24293517c2e04a2", + "to": "account-hash-1265caa7cd80f31f882ab6f5623d89741c77cf3a36a309b54ed55fdc0be227c9", + "source": "uref-c003c853c6477137225914c7610cf2c30ed594480c1fccd6dfd58a633522edc6-007", + "target": "uref-c74ad99f2bca136c1e64b1a14bf576696363c25701d66d87adca6dbdb147b39d-004", + "amount": "2500000000000", + "gas": "0", + "id": null + } + }, + "merkle_proof": "0100000003c74ad99f2bca136c1e64b1a14bf576696363c25701d66d87adca6dbdb147b39d05cd91f138e82ddce5dfbb99c6bbf3f47caca439b81d7f43702cbebfa99bacbfd087516c22bca9a14179ebbbe646c8f911153fe53626126c1ba24293517c2e04a2011265caa7cd80f31f882ab6f5623d89741c77cf3a36a309b54ed55fdc0be227c9c003c853c6477137225914c7610cf2c30ed594480c1fccd6dfd58a633522edc607c74ad99f2bca136c1e64b1a14bf576696363c25701d66d87adca6dbdb147b39d040600a89c13460200000200000000c7080000000400a8a2c8abb44aa2e88169f0b9354b1db7fc7300fc5cbb860c52802dfeca0a39d70d000a3d7f57c86a55ffcf45899071404663e9c85b3c26644d0861724809836edff00e00f04d9ff40974591c3de3bb5e3355b60fd3b7f0703eb28213904a31ba58f1dc9514003edffabc50e557526ff9c01b1fa581cf560f8b1b57d5ee00ea072d55e9b2b1a0310050992ed0b3b63bcc2f33ad04c203de33b9a91c32abb4ee7b17c59cdf99f899df8600768b619b425f98445824cfeff827dbbb6cd4a598e222b630a455b892a077bdd7ef012460796877e7106f67da2e5d3206d97a020d1e1f442403d714ffd3a1d7eb0bdcf400507226381d48af14f08767414cbbb2479c0637e892c93dd332f4eb9623f05c5100031100000000017da10bd7dcd1d89149ff1765dcbbad7b57231b2ab227d8af7dda4f4c7eda3c390100b2c12a9e61379047b4bfbe9beb64a7782f4b6b87647459e34ea487f189c738d80201536a37fd00820c285355734a3dfcf094407487d4530609979a8d4722bece6a5604014c76c9f2caf55d8962aa32b3fdd5f887bad4f92d2a12728962bac8dd03f5e7be06012790a10653f15923b90ceaefbeb22ace1ed083ee4b36a8cdd2f4605562a6e7930a0032bd6991fdc3e5d108acb5e9860f87e521b11ebe8744722bdd8b8febedd495620b0010d358322b226099302b72fc1dcee15ef2c5182633078f9b261cecbcf38ba0490c01ed825da87ad90573bc714a1c312e749a66a5fb11643d09aa13505ad3523ec01f0d004d7d61e4bccc295f9e749e699c686ea72bda64f9c262966edb281efbaaed25e60e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f015fda27549535d581e35954d9fec577972a6b3921b4bea74ace58f971bc44ef37100149d5e7bfe2e0b8b01d44f53adf40b77d391b9d008d66be8ca6e8982058d4db8e110162e3c98c88cd12b4ba47f3c59158b5aef048eecab639ac055c69025388a2020e120188c216f76544d3c1fbefc85148e5c62b31901da4a5e2680c526ac8662281ad8f140140d93937739fa8ca0fce0cd9dfcc6fc440b1400b6c730978b2e26a277bc397041500637d03dd4f755905fe5ef5757294211766db21e29ebb7cc3d29c494bc335e1ea1701e77b15a40bde97cdd4200b3c02b4683314d794f95fb9d0eaab1041187feb13f7" +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Delegator.cs b/Casper.Network.SDK/Types/Delegator.cs index 1c45be9..2ab5a9e 100644 --- a/Casper.Network.SDK/Types/Delegator.cs +++ b/Casper.Network.SDK/Types/Delegator.cs @@ -82,45 +82,69 @@ public override List Read( JsonSerializerOptions options) { var delegators = new List(); - - if (reader.TokenType != JsonTokenType.StartArray) - throw new JsonException("StartArray token expected to deserialize a list of delegators."); - - reader.Read(); - - while (reader.TokenType != JsonTokenType.EndArray) + + if (reader.TokenType == JsonTokenType.StartArray) { - try + reader.Read(); + + while (reader.TokenType != JsonTokenType.EndArray) { - using (JsonDocument document = JsonDocument.ParseValue(ref reader)) + try { - if (document.RootElement.TryGetProperty("delegator_public_key", out var delegatorPublicKey)) + using (JsonDocument document = JsonDocument.ParseValue(ref reader)) { - var pkAndDelegator = JsonSerializer.Deserialize(document.RootElement.GetRawText()); - delegators.Add(pkAndDelegator.Delegator); - } - else - { - var pkAndDelegator = JsonSerializer.Deserialize(document.RootElement.GetRawText()); - delegators.Add(new Delegator() + if (document.RootElement.TryGetProperty("delegator_public_key", + out var delegatorPublicKey)) + { + var pkAndDelegator = + JsonSerializer.Deserialize(document.RootElement + .GetRawText()); + delegators.Add(pkAndDelegator.Delegator); + } + else { - BondingPurse = pkAndDelegator.BondingPurse, - StakedAmount = pkAndDelegator.StakedAmount, - DelegatorPublicKey = pkAndDelegator.DelegatorPublicKey, - ValidatorPublicKey = pkAndDelegator.ValidatorPublicKey, - }); + var pkAndDelegator = + JsonSerializer.Deserialize(document.RootElement.GetRawText()); + delegators.Add(new Delegator() + { + BondingPurse = pkAndDelegator.BondingPurse, + StakedAmount = pkAndDelegator.StakedAmount, + DelegatorPublicKey = pkAndDelegator.DelegatorPublicKey, + ValidatorPublicKey = pkAndDelegator.ValidatorPublicKey, + }); + } } - } - reader.Read(); // skip end object + reader.Read(); // skip end object + } + catch (Exception e) + { + throw new JsonException("Cannot parse list of delegators. " + e.Message); + } } - catch (Exception e) + + return delegators; + } + + if (reader.TokenType == JsonTokenType.StartObject) + { + reader.Read(); // skip start object + + while (reader.TokenType == JsonTokenType.PropertyName) { - throw new JsonException("Cannot parse list of delegators. " + e.Message); + reader.Read(); // skip public key + + var delegator = + JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + delegators.Add(delegator); } + + return delegators; } + + throw new JsonException("StartArray or StartObject token expected to deserialize a list of delegators."); - return delegators; } public override void Write( diff --git a/Casper.Network.SDK/Types/StoredValue.cs b/Casper.Network.SDK/Types/StoredValue.cs index 1117fe2..8b19662 100644 --- a/Casper.Network.SDK/Types/StoredValue.cs +++ b/Casper.Network.SDK/Types/StoredValue.cs @@ -22,7 +22,7 @@ public class StoredValue public ContractPackage ContractPackage { get; init; } - public Transfer LegacyTransfer { get; init; } + public Transfer Transfer { get; init; } public DeployInfo DeployInfo { get; init; } @@ -94,13 +94,27 @@ JsonSerializerOptions options throw new JsonException("Cannot deserialize StoredValue. PropertyName expected."); var propertyName = reader.GetString(); + var propertyInfo = typeof(StoredValue).GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); - if (propertyInfo == null) + if (propertyInfo != null) + { + reader.Read(); + var propertyValue = JsonSerializer.Deserialize(ref reader, propertyInfo.PropertyType, options); + propertyInfo.SetValue(storedValue, propertyValue); + } + else if(propertyName.Equals("LegacyTransfer", StringComparison.InvariantCultureIgnoreCase)) + { + reader.Read(); + + var serializerOptions = new JsonSerializerOptions(options); + serializerOptions.Converters.Add(new Transfer.TransferConverter()); + + var t = JsonSerializer.Deserialize(ref reader, serializerOptions); + propertyInfo = typeof(StoredValue).GetProperty("Transfer", BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); + propertyInfo?.SetValue(storedValue, t); + } + else throw new JsonException($"Unknown property: {propertyName}."); - - reader.Read(); - var propertyValue = JsonSerializer.Deserialize(ref reader, propertyInfo.PropertyType, options); - propertyInfo.SetValue(storedValue, propertyValue); } return storedValue; diff --git a/Casper.Network.SDK/Types/Transfer.cs b/Casper.Network.SDK/Types/Transfer.cs index e35890d..2e37067 100644 --- a/Casper.Network.SDK/Types/Transfer.cs +++ b/Casper.Network.SDK/Types/Transfer.cs @@ -69,6 +69,7 @@ public class TransferV1 /// /// Represents a version 2 transfer from one purse to another /// + [JsonConverter(typeof(TransferConverter))] public class Transfer { protected int _version; From 77ff5dbbc8c2de95d7d1ac2b250f01c9bf1e383b Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 11 Jul 2024 20:06:22 +0200 Subject: [PATCH 062/126] Update integration tests to fix issues due to changes in the SDK. Signed-off-by: David Hernando --- .../CLValueJsonDeserializerTest.cs | 8 +-- .../Casper.Network.SDK.Test.csproj | 9 +-- .../DeployJsonSerializerTest.cs | 2 +- Casper.Network.SDK.Test/NctlContractTest.cs | 9 ++- Casper.Network.SDK.Test/NctlGetXTest.cs | 59 +++++++------------ .../NctlMyDictContractTest.cs | 13 ++-- Casper.Network.SDK.Test/RpcResultTest.cs | 50 ++++++++-------- .../ServerEventsClientTest.cs | 7 ++- 8 files changed, 70 insertions(+), 87 deletions(-) diff --git a/Casper.Network.SDK.Test/CLValueJsonDeserializerTest.cs b/Casper.Network.SDK.Test/CLValueJsonDeserializerTest.cs index 4862944..364026c 100644 --- a/Casper.Network.SDK.Test/CLValueJsonDeserializerTest.cs +++ b/Casper.Network.SDK.Test/CLValueJsonDeserializerTest.cs @@ -191,7 +191,7 @@ public void SerializeU512CLValue() var doc = System.Text.Json.JsonDocument.Parse(json); Assert.AreEqual(3, doc.RootElement.EnumerateObject().Count()); Assert.AreEqual("U512", doc.RootElement.GetProperty("cl_type").GetString()); - Assert.AreEqual("05005550B405", doc.RootElement.GetProperty("bytes").GetString()); + Assert.AreEqual("05005550b405", doc.RootElement.GetProperty("bytes").GetString().ToLower()); Assert.AreEqual("24500000000", doc.RootElement.GetProperty("parsed").GetString()); clValue = CLValue.U512(15000000000); @@ -541,7 +541,7 @@ public void SerializeKeyCLValue() Assert.IsNotEmpty(json); Assert.IsTrue(json.Contains(@"""cl_type"":""Key""")); Assert.IsTrue(json.Contains(@"""bytes"":""08b192")); - Assert.IsTrue(json.Contains(@"""parsed"":{""Withdraw"":""withdraw-B192")); + Assert.IsTrue(json.Contains(@"""parsed"":{""Withdraw"":""withdraw-b192")); gsKey = GlobalStateKey.FromString( "uref-e48935c79e96c490c01e1e8800de5ec5f4a857a57db0dcffed1e1e2b5d29b5e4-007"); @@ -550,9 +550,9 @@ public void SerializeKeyCLValue() Assert.IsNotEmpty(json); Assert.IsTrue(json.Contains(@"""cl_type"":""Key""")); Assert.IsTrue( - json.Contains(@"""bytes"":""02e48935c79E96C490c01e1E8800dE5Ec5F4A857A57dB0DcFfED1e1E2b5d29b5E407")); + json.Contains(@"""bytes"":""02e48935c79e96c490c01e1e8800de5ec5f4a857a57db0dcffed1e1e2b5d29b5e407")); Assert.IsTrue(json.Contains( - @"""parsed"":{""URef"":""uref-E48935C79E96c490c01e1E8800de5Ec5F4A857A57db0dcffEd1E1E2B5D29b5E4-007")); + @"""parsed"":{""URef"":""uref-e48935c79e96c490c01e1e8800de5ec5f4a857a57db0dcffed1e1e2b5d29b5e4-007")); gsKey = GlobalStateKey.FromString("era-2685"); clValue = CLValue.Key(gsKey); diff --git a/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj b/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj index e3d61c1..14d4bf2 100644 --- a/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj +++ b/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj @@ -33,13 +33,6 @@ - - - - - - - - + diff --git a/Casper.Network.SDK.Test/DeployJsonSerializerTest.cs b/Casper.Network.SDK.Test/DeployJsonSerializerTest.cs index a672517..d02c4c4 100644 --- a/Casper.Network.SDK.Test/DeployJsonSerializerTest.cs +++ b/Casper.Network.SDK.Test/DeployJsonSerializerTest.cs @@ -12,7 +12,7 @@ public class DeployJsonSerializer private string deploy1 = TestContext.CurrentContext.TestDirectory + "/TestData/transfer-deploy.json"; private string signer1 = "017b8058863Aad49c7b89c77019ceF3a4D863BDf1c0c61499776F94b18465810F7"; private string signature1 = - "012a0c5896aB9D6Cf029268Cf454Ba9D42bcD40Bd909C984890F326739EeF13201d1d2a9a95938B2a966F2E650D1bDd80931F0374e3A92403e025B806Aa1065109"; + "012a0c5896ab9d6cf029268cf454ba9d42bcd40bd909c984890f326739eef13201d1d2a9a95938b2a966f2e650d1bdd80931f0374e3a92403e025b806aa1065109"; [Test] public void LoadDeployFromFileTest() diff --git a/Casper.Network.SDK.Test/NctlContractTest.cs b/Casper.Network.SDK.Test/NctlContractTest.cs index df520d2..dd0e033 100644 --- a/Casper.Network.SDK.Test/NctlContractTest.cs +++ b/Casper.Network.SDK.Test/NctlContractTest.cs @@ -50,7 +50,8 @@ public async Task WaitContractDeploymentTest() var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); var getResponse = await _client.GetDeploy(_contractDeployHash, tokenSource.Token); - var execResult = getResponse.Parse().ExecutionResults.First(); + var execInfo = getResponse.Parse().ExecutionInfo; + var execResult = (ExecutionResultV1)execInfo.ExecutionResult; Assert.IsTrue(execResult.IsSuccess); Assert.AreEqual(64, execResult.BlockHash.Length); Assert.IsNull(execResult.ErrorMessage); @@ -102,7 +103,8 @@ public async Task CallContractByNameTest() var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); var getResponse = await _client.GetDeploy(deployHash, tokenSource.Token); - var execResult = getResponse.Parse().ExecutionResults.First(); + var execInfo = getResponse.Parse().ExecutionInfo; + var execResult = (ExecutionResultV1)execInfo.ExecutionResult; Assert.IsTrue(execResult.IsSuccess); } @@ -127,7 +129,8 @@ public async Task CallContractByHashTest() var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); var getResponse = await _client.GetDeploy(deployHash, tokenSource.Token); - var execResult = getResponse.Parse().ExecutionResults.First(); + var execInfo = getResponse.Parse().ExecutionInfo; + var execResult = (ExecutionResultV1)execInfo.ExecutionResult; Assert.IsTrue(execResult.IsSuccess); _blockHash = execResult.BlockHash; Assert.IsNotNull(_blockHash); diff --git a/Casper.Network.SDK.Test/NctlGetXTest.cs b/Casper.Network.SDK.Test/NctlGetXTest.cs index 1dda357..6a144a8 100644 --- a/Casper.Network.SDK.Test/NctlGetXTest.cs +++ b/Casper.Network.SDK.Test/NctlGetXTest.cs @@ -47,7 +47,7 @@ public async Task GetAccountTest() try { var block = (await _client.GetBlock()).Parse().Block; - var blockHeight = (int) block.Header.Height; + var blockHeight = (int) block.Height; var blockHash = block.Hash; var stateRootHash = await _client.GetStateRootHash(blockHash); @@ -75,46 +75,31 @@ public async Task GetAccountTest() var accountInfo6 = response6.Parse(); Assert.AreEqual(accountInfo.Account.AccountHash.ToString(), accountInfo6.Account.AccountHash.ToString()); - var resp = await _client.GetAccountBalance(_faucetKey.PublicKey, stateRootHash); + var resp = await _client.GetBalance(accountInfo.Account.MainPurse.ToString(), stateRootHash); var accountBalance = resp.Parse(); - Assert.IsTrue(accountBalance.BalanceValue > 0); - - resp = await _client.GetAccountBalance(accountInfo.Account.MainPurse, stateRootHash); - var accountBalanceCompare = resp.Parse(); - Assert.AreEqual(accountBalance.BalanceValue, accountBalanceCompare.BalanceValue); - - resp = await _client.GetAccountBalance(accountInfo.Account.MainPurse.ToString(), stateRootHash); - accountBalanceCompare = resp.Parse(); - Assert.AreEqual(accountBalance.BalanceValue, accountBalanceCompare.BalanceValue); - resp = await _client.GetAccountBalance(new AccountHashKey(_faucetKey.PublicKey), stateRootHash); - accountBalanceCompare = resp.Parse(); - Assert.AreEqual(accountBalance.BalanceValue, accountBalanceCompare.BalanceValue); - - - resp = await _client.GetAccountBalance(_faucetKey.PublicKey, blockHeight); - accountBalanceCompare = resp.Parse(); + var respq = await _client.QueryBalance(_faucetKey.PublicKey, (ulong)blockHeight); + var accountBalanceCompare = respq.Parse(); Assert.AreEqual(accountBalance.BalanceValue, accountBalanceCompare.BalanceValue); - resp = await _client.GetAccountBalance(accountInfo.Account.MainPurse, blockHeight); - accountBalanceCompare = resp.Parse(); + respq = await _client.QueryBalance(accountInfo.Account.MainPurse, (ulong)blockHeight); + accountBalanceCompare = respq.Parse(); Assert.AreEqual(accountBalance.BalanceValue, accountBalanceCompare.BalanceValue); - resp = await _client.GetAccountBalance(new AccountHashKey(_faucetKey.PublicKey), blockHeight); - accountBalanceCompare = resp.Parse(); + respq = await _client.QueryBalance(new AccountHashKey(_faucetKey.PublicKey), (ulong)blockHeight); + accountBalanceCompare = respq.Parse(); Assert.AreEqual(accountBalance.BalanceValue, accountBalanceCompare.BalanceValue); - - resp = await _client.GetAccountBalanceWithBlockHash(_faucetKey.PublicKey, blockHash); - accountBalanceCompare = resp.Parse(); + respq = await _client.QueryBalance(_faucetKey.PublicKey, blockHash); + accountBalanceCompare = respq.Parse(); Assert.AreEqual(accountBalance.BalanceValue, accountBalanceCompare.BalanceValue); - resp = await _client.GetAccountBalanceWithBlockHash(accountInfo.Account.MainPurse, blockHash); - accountBalanceCompare = resp.Parse(); + respq = await _client.QueryBalance(accountInfo.Account.MainPurse, blockHash); + accountBalanceCompare = respq.Parse(); Assert.AreEqual(accountBalance.BalanceValue, accountBalanceCompare.BalanceValue); - resp = await _client.GetAccountBalanceWithBlockHash(new AccountHashKey(_faucetKey.PublicKey), blockHash); - accountBalanceCompare = resp.Parse(); + respq = await _client.QueryBalance(new AccountHashKey(_faucetKey.PublicKey), blockHash); + accountBalanceCompare = respq.Parse(); Assert.AreEqual(accountBalance.BalanceValue, accountBalanceCompare.BalanceValue); } catch (RpcClientException e) @@ -139,7 +124,7 @@ public async Task GetAccountTest() { var key1 = KeyPair.CreateNew(KeyAlgo.ED25519); - await _client.GetAccountBalance(key1.PublicKey); + await _client.QueryBalance(key1.PublicKey); Assert.Fail("Exception expected"); } catch (RpcClientException e) @@ -162,13 +147,13 @@ public async Task GetBlockTest() var result2 = response2.Parse(); Assert.IsNotNull(result2.Block.Hash); - Assert.AreEqual(result2.Block.Body.Proposer.IsSystem, result2.Block.Body.Proposer.isSystem); + Assert.AreEqual(result2.Block.Proposer.IsSystem, result2.Block.Proposer.isSystem); var response3 = await _client.GetBlock(result2.Block.Hash); var result3 = response3.Parse(); Assert.AreEqual(result2.Block.Hash, result3.Block.Hash); - var response4 = await _client.GetBlock((int)result2.Block.Header.Height); + var response4 = await _client.GetBlock((int)result2.Block.Height); var result4 = response4.Parse(); Assert.AreEqual(result2.Block.Hash, result4.Block.Hash); @@ -176,14 +161,14 @@ public async Task GetBlockTest() var result5 = response5.Parse(); Assert.AreEqual(0, result5.Transfers.Count); - var response6 = await _client.GetBlockTransfers((int)result2.Block.Header.Height); + var response6 = await _client.GetBlockTransfers((int)result2.Block.Height); var result6 = response6.Parse(); Assert.AreEqual(0, result6.Transfers.Count); var hash1 = await _client.GetStateRootHash(result2.Block.Hash); Assert.AreEqual(32*2, hash1.Length); - var hash2 = await _client.GetStateRootHash((int)result2.Block.Header.Height); + var hash2 = await _client.GetStateRootHash((int)result2.Block.Height); Assert.AreEqual(hash1, hash2); var hash3 = await _client.GetStateRootHash(); @@ -216,8 +201,8 @@ public async Task GetSystemBlockProposerTest() var response = await _client.GetBlock(0); var result = response.Parse(); Assert.IsNotNull(result.Block.Hash); - Assert.IsTrue(result.Block.Body.Proposer.IsSystem); - Assert.AreEqual(result.Block.Body.Proposer.IsSystem, result.Block.Body.Proposer.isSystem); + Assert.IsTrue(result.Block.Proposer.IsSystem); + Assert.AreEqual(result.Block.Proposer.IsSystem, result.Block.Proposer.isSystem); } catch (RpcClientException e) { @@ -276,7 +261,7 @@ public async Task GetEraSummaryTest() var result3 = response3.Parse(); Assert.IsNotNull(result3.Block.Hash); - var response4 = await _client.GetEraSummary((int)result3.Block.Header.Height); + var response4 = await _client.GetEraSummary((int)result3.Block.Height); var result4 = response4.Parse(); Assert.IsTrue(result4.EraSummary.EraId > 0); Assert.IsNotNull(result4.EraSummary.StoredValue.EraInfo); diff --git a/Casper.Network.SDK.Test/NctlMyDictContractTest.cs b/Casper.Network.SDK.Test/NctlMyDictContractTest.cs index b11f659..8248bea 100644 --- a/Casper.Network.SDK.Test/NctlMyDictContractTest.cs +++ b/Casper.Network.SDK.Test/NctlMyDictContractTest.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -8,7 +7,6 @@ using Casper.Network.SDK.Types; using Casper.Network.SDK.Utils; using NUnit.Framework; -using Org.BouncyCastle.Utilities.Encoders; namespace NetCasperTest { @@ -49,9 +47,10 @@ public async Task WaitContractDeploymentTest() var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); var getResponse = await _client.GetDeploy(_contractDeployHash, tokenSource.Token); - var execResult = getResponse.Parse().ExecutionResult; + var execInfo = getResponse.Parse().ExecutionInfo; + var execResult = execInfo.ExecutionResult; Assert.IsTrue(execResult.IsSuccess); - Assert.AreEqual(64, execResult.BlockHash.Length); + AssertExtensions.IsHash(execInfo.BlockHash); Assert.IsNull(execResult.ErrorMessage); } @@ -81,7 +80,8 @@ public async Task CallVersionedContractByNameTest() var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); var getResponse = await _client.GetDeploy(deployHash, tokenSource.Token); - var execResult = getResponse.Parse().ExecutionResults.First(); + var execInfo = getResponse.Parse().ExecutionInfo; + var execResult = execInfo.ExecutionResult; Assert.IsTrue(execResult.IsSuccess); } @@ -123,7 +123,8 @@ public async Task CallVersionedContractByHashTest() var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); var getResponse = await _client.GetDeploy(deployHash, tokenSource.Token); - var execResult = getResponse.Parse().ExecutionResults.First(); + var execInfo = getResponse.Parse().ExecutionInfo; + var execResult = execInfo.ExecutionResult; Assert.IsTrue(execResult.IsSuccess); } diff --git a/Casper.Network.SDK.Test/RpcResultTest.cs b/Casper.Network.SDK.Test/RpcResultTest.cs index 1d42b19..4dfd35a 100644 --- a/Casper.Network.SDK.Test/RpcResultTest.cs +++ b/Casper.Network.SDK.Test/RpcResultTest.cs @@ -41,29 +41,29 @@ public void SuccessExecutionResultDeserializerTest() JsonSerializerOptions options = new JsonSerializerOptions() { WriteIndented = true, - Converters = {new ExecutionResult.ExecutionResultConverter()} + Converters = {new ExecutionResultV1.ExecutionResultV1Converter()} }; - var results = JsonSerializer.Deserialize(json, options); - Assert.IsNotNull(results); - Assert.IsTrue(results.IsSuccess); - Assert.IsNull(results.ErrorMessage); - Assert.AreEqual(System.Numerics.BigInteger.Parse("42905260"), results.Cost); - Assert.AreEqual(0, results.Transfers.Count); + var resultsV1 = JsonSerializer.Deserialize(json, options); + Assert.IsNotNull(resultsV1); + Assert.IsTrue(resultsV1.IsSuccess); + Assert.IsNull(resultsV1.ErrorMessage); + Assert.AreEqual(System.Numerics.BigInteger.Parse("42905260"), resultsV1.Cost); + Assert.AreEqual(0, resultsV1.Transfers.Count); - Assert.AreEqual(10, results.Effect.Operations.Count); + Assert.AreEqual(10, resultsV1.Effect.Operations.Count); Assert.AreEqual("hash-efef434ae3cea598a8871ac40ac5d5b00f0f4babe8f7b49a05f87d203d1c8391", - results.Effect.Operations[0].Key.ToString().ToLower()); - Assert.AreEqual(OpKind.Read, results.Effect.Operations[0].Kind); + resultsV1.Effect.Operations[0].Key.ToString().ToLower()); + Assert.AreEqual(OpKind.Read, resultsV1.Effect.Operations[0].Kind); Assert.AreEqual("balance-d86ed76303a691d12cf121e4b4cb4fd875484f28a33c2edf14aad56c01c8c601", - results.Effect.Operations[9].Key.ToString().ToLower()); - Assert.AreEqual(OpKind.Write, results.Effect.Operations[9].Kind); + resultsV1.Effect.Operations[9].Key.ToString().ToLower()); + Assert.AreEqual(OpKind.Write, resultsV1.Effect.Operations[9].Kind); - Assert.AreEqual(10, results.Effect.Transforms.Count); + Assert.AreEqual(10, resultsV1.Effect.Transforms.Count); Assert.AreEqual("hash-98eca31f263ff3176518b6dcbb6af54c7469b60192749feee073d20618a389e6", - results.Effect.Transforms[0].Key.ToString().ToLower() + resultsV1.Effect.Transforms[0].Key.ToString().ToLower() ); Assert.AreEqual("uref-a3a79be7bd922c0846ed406b01f2430dd6ba367a2703b8d75109a9f41fdc336b-000", - results.Effect.Transforms[9].Key.ToString().ToLower() + resultsV1.Effect.Transforms[9].Key.ToString().ToLower() ); } @@ -76,19 +76,19 @@ public void FailureExecutionResultDeserializerTest() JsonSerializerOptions options = new JsonSerializerOptions() { WriteIndented = true, - Converters = {new ExecutionResult.ExecutionResultConverter()} + Converters = {new ExecutionResultV1.ExecutionResultV1Converter()} }; - var results = JsonSerializer.Deserialize(json, options); - Assert.IsNotNull(results); - Assert.IsFalse(results.IsSuccess); - Assert.IsNotNull(results.ErrorMessage); + var resultsV1 = JsonSerializer.Deserialize(json, options); + Assert.IsNotNull(resultsV1); + Assert.IsFalse(resultsV1.IsSuccess); + Assert.IsNotNull(resultsV1.ErrorMessage); Assert.AreEqual( "Wasm preprocessing error: Encountered operation forbidden by gas rules. Consult instruction -> metering config map", - results.ErrorMessage); - Assert.AreEqual(BigInteger.Zero, results.Cost); - Assert.AreEqual(0, results.Transfers.Count); - Assert.AreEqual(0, results.Effect.Operations.Count); - Assert.AreEqual(0, results.Effect.Transforms.Count); + resultsV1.ErrorMessage); + Assert.AreEqual(BigInteger.Zero, resultsV1.Cost); + Assert.AreEqual(0, resultsV1.Transfers.Count); + Assert.AreEqual(0, resultsV1.Effect.Operations.Count); + Assert.AreEqual(0, resultsV1.Effect.Transforms.Count); } [Test] diff --git a/Casper.Network.SDK.Test/ServerEventsClientTest.cs b/Casper.Network.SDK.Test/ServerEventsClientTest.cs index 9c06fe8..bba60af 100644 --- a/Casper.Network.SDK.Test/ServerEventsClientTest.cs +++ b/Casper.Network.SDK.Test/ServerEventsClientTest.cs @@ -103,7 +103,8 @@ private async Task MakeTransfer() var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); var getResponse = await _client.GetDeploy(deploy.Hash, tokenSource.Token); - var execResult = getResponse.Parse().ExecutionResults.First(); + var execInfo = getResponse.Parse().ExecutionInfo; + var execResult = (ExecutionResultV1)execInfo.ExecutionResult; Assert.IsTrue(execResult.IsSuccess); } @@ -167,10 +168,10 @@ public async Task SSEListen() await MakeTransfer(); int retries = 0; - while(nDeployProcessed == 0 && retries++ < 5) + while(nDeployProcessed == 0 && retries++ < 3) Thread.Sleep(5000); - sse.StopListening().Wait(); + await sse.StopListening(); Assert.IsTrue(nApiVersion > 0); Assert.IsTrue(nBlocks > 0); From 8aba8baf9a5dc5bf84f9bd6586d40195446f97fc Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 12 Jul 2024 09:33:57 +0200 Subject: [PATCH 063/126] fixed TransformV1 to Transform cast fixed TransferV2 parsing Signed-off-by: David Hernando --- Casper.Network.SDK/Types/Transfer.cs | 7 ++++++- Casper.Network.SDK/Types/Transform.cs | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Casper.Network.SDK/Types/Transfer.cs b/Casper.Network.SDK/Types/Transfer.cs index 2e37067..5a5794d 100644 --- a/Casper.Network.SDK/Types/Transfer.cs +++ b/Casper.Network.SDK/Types/Transfer.cs @@ -190,7 +190,7 @@ public override Transfer Read( if (root.TryGetProperty("Version2", out JsonElement v2Element)) { - var transfer = JsonSerializer.Deserialize(v2Element.GetRawText()); + var transfer = JsonSerializer.Deserialize(v2Element.GetRawText()); return transfer; } @@ -230,4 +230,9 @@ public override void Write( } } } + + internal class TransferV2 : Transfer + { + + } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Transform.cs b/Casper.Network.SDK/Types/Transform.cs index 931ca2f..48b26fd 100644 --- a/Casper.Network.SDK/Types/Transform.cs +++ b/Casper.Network.SDK/Types/Transform.cs @@ -128,6 +128,8 @@ public static explicit operator Transform(TransformV1 transform) return new Transform { + _version = 1, + _transformV1 = transform, Key = transform.Key, TransformKind = kind, Value = transform.Value, From 3316768d1bd3b1ac5ebaf182da5336bd8b34c182 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 12 Jul 2024 09:40:00 +0200 Subject: [PATCH 064/126] Update versions to .NET 8 Fixes CVE-2024-30105 - System.Text.Json vulnerability Signed-off-by: David Hernando --- .../Casper.Network.SDK.Test.csproj | 12 ++++++------ Casper.Network.SDK/Casper.Network.SDK.csproj | 6 +++--- Docs/Examples/AwaitEvents/AwaitEvents.csproj | 2 +- Docs/Examples/DelegateStake/DelegateStake.csproj | 2 +- Docs/Examples/ExecTransfer/ExecTransfer.csproj | 2 +- .../GetAccountBalance/GetAccountBalance.csproj | 2 +- .../GetBlockTransfers/GetBlockTransfers.csproj | 2 +- Docs/Examples/GetNodeMetrics/GetNodeMetrics.csproj | 2 +- Docs/Examples/GetNodeStatus/GetNodeStatus.csproj | 2 +- Docs/Examples/ListRewards/ListRewards.csproj | 2 +- .../counter-contract/CounterContract.csproj | 2 +- Docs/Tutorials/erc20-contract/erc20Contract.csproj | 2 +- .../kvstorage-contract/KVStorageContract.csproj | 2 +- 13 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj b/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj index 14d4bf2..cbbea81 100644 --- a/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj +++ b/Casper.Network.SDK.Test/Casper.Network.SDK.Test.csproj @@ -1,15 +1,15 @@ - net7.0 + net8.0 9.0 false NetCasperTest - - + + @@ -18,11 +18,11 @@ $(TEST_FRAMEWORK) - net7.0 + net8.0 - - + + diff --git a/Casper.Network.SDK/Casper.Network.SDK.csproj b/Casper.Network.SDK/Casper.Network.SDK.csproj index ec45be5..2f98c87 100644 --- a/Casper.Network.SDK/Casper.Network.SDK.csproj +++ b/Casper.Network.SDK/Casper.Network.SDK.csproj @@ -1,7 +1,7 @@ - net7.0;netstandard2.0 + net8.0;netstandard2.0 9.0 CS1591 2.3.0.0 @@ -25,8 +25,8 @@ - - + + diff --git a/Docs/Examples/AwaitEvents/AwaitEvents.csproj b/Docs/Examples/AwaitEvents/AwaitEvents.csproj index 9fdc273..1dfa19e 100644 --- a/Docs/Examples/AwaitEvents/AwaitEvents.csproj +++ b/Docs/Examples/AwaitEvents/AwaitEvents.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/Docs/Examples/DelegateStake/DelegateStake.csproj b/Docs/Examples/DelegateStake/DelegateStake.csproj index 9fdc273..1dfa19e 100644 --- a/Docs/Examples/DelegateStake/DelegateStake.csproj +++ b/Docs/Examples/DelegateStake/DelegateStake.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/Docs/Examples/ExecTransfer/ExecTransfer.csproj b/Docs/Examples/ExecTransfer/ExecTransfer.csproj index 9fdc273..1dfa19e 100644 --- a/Docs/Examples/ExecTransfer/ExecTransfer.csproj +++ b/Docs/Examples/ExecTransfer/ExecTransfer.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/Docs/Examples/GetAccountBalance/GetAccountBalance.csproj b/Docs/Examples/GetAccountBalance/GetAccountBalance.csproj index 9fdc273..1dfa19e 100644 --- a/Docs/Examples/GetAccountBalance/GetAccountBalance.csproj +++ b/Docs/Examples/GetAccountBalance/GetAccountBalance.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/Docs/Examples/GetBlockTransfers/GetBlockTransfers.csproj b/Docs/Examples/GetBlockTransfers/GetBlockTransfers.csproj index 9fdc273..1dfa19e 100644 --- a/Docs/Examples/GetBlockTransfers/GetBlockTransfers.csproj +++ b/Docs/Examples/GetBlockTransfers/GetBlockTransfers.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/Docs/Examples/GetNodeMetrics/GetNodeMetrics.csproj b/Docs/Examples/GetNodeMetrics/GetNodeMetrics.csproj index 9fdc273..1dfa19e 100644 --- a/Docs/Examples/GetNodeMetrics/GetNodeMetrics.csproj +++ b/Docs/Examples/GetNodeMetrics/GetNodeMetrics.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/Docs/Examples/GetNodeStatus/GetNodeStatus.csproj b/Docs/Examples/GetNodeStatus/GetNodeStatus.csproj index 9fdc273..1dfa19e 100644 --- a/Docs/Examples/GetNodeStatus/GetNodeStatus.csproj +++ b/Docs/Examples/GetNodeStatus/GetNodeStatus.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/Docs/Examples/ListRewards/ListRewards.csproj b/Docs/Examples/ListRewards/ListRewards.csproj index 9fdc273..1dfa19e 100644 --- a/Docs/Examples/ListRewards/ListRewards.csproj +++ b/Docs/Examples/ListRewards/ListRewards.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/Docs/Tutorials/counter-contract/CounterContract.csproj b/Docs/Tutorials/counter-contract/CounterContract.csproj index 9fdc273..1dfa19e 100644 --- a/Docs/Tutorials/counter-contract/CounterContract.csproj +++ b/Docs/Tutorials/counter-contract/CounterContract.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/Docs/Tutorials/erc20-contract/erc20Contract.csproj b/Docs/Tutorials/erc20-contract/erc20Contract.csproj index 9fdc273..1dfa19e 100644 --- a/Docs/Tutorials/erc20-contract/erc20Contract.csproj +++ b/Docs/Tutorials/erc20-contract/erc20Contract.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/Docs/Tutorials/kvstorage-contract/KVStorageContract.csproj b/Docs/Tutorials/kvstorage-contract/KVStorageContract.csproj index 9fdc273..1dfa19e 100644 --- a/Docs/Tutorials/kvstorage-contract/KVStorageContract.csproj +++ b/Docs/Tutorials/kvstorage-contract/KVStorageContract.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 From ba45df8780c9394123f1194613a4f8cc5dfc5fad Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 12 Jul 2024 12:21:14 +0200 Subject: [PATCH 065/126] replace existing `int blockHeight` arguments with the proper `ulong blockheight`. Signed-off-by: David Hernando --- Casper.Network.SDK.Test/NctlGetXTest.cs | 14 ++++---- Casper.Network.SDK/ICasperClient.cs | 38 ++++++++++++++++----- Casper.Network.SDK/JsonRpc/CasperMethods.cs | 18 +++++----- Casper.Network.SDK/JsonRpc/RpcMethod.cs | 4 +-- Casper.Network.SDK/NetCasperClient.cs | 18 +++++----- 5 files changed, 57 insertions(+), 35 deletions(-) diff --git a/Casper.Network.SDK.Test/NctlGetXTest.cs b/Casper.Network.SDK.Test/NctlGetXTest.cs index 6a144a8..34a28c9 100644 --- a/Casper.Network.SDK.Test/NctlGetXTest.cs +++ b/Casper.Network.SDK.Test/NctlGetXTest.cs @@ -47,7 +47,7 @@ public async Task GetAccountTest() try { var block = (await _client.GetBlock()).Parse().Block; - var blockHeight = (int) block.Height; + var blockHeight = block.Height; var blockHash = block.Hash; var stateRootHash = await _client.GetStateRootHash(blockHash); @@ -147,13 +147,13 @@ public async Task GetBlockTest() var result2 = response2.Parse(); Assert.IsNotNull(result2.Block.Hash); - Assert.AreEqual(result2.Block.Proposer.IsSystem, result2.Block.Proposer.isSystem); + Assert.AreEqual(result2.Block.Proposer.IsSystem, result2.Block.Proposer.IsSystem); var response3 = await _client.GetBlock(result2.Block.Hash); var result3 = response3.Parse(); Assert.AreEqual(result2.Block.Hash, result3.Block.Hash); - var response4 = await _client.GetBlock((int)result2.Block.Height); + var response4 = await _client.GetBlock(result2.Block.Height); var result4 = response4.Parse(); Assert.AreEqual(result2.Block.Hash, result4.Block.Hash); @@ -161,14 +161,14 @@ public async Task GetBlockTest() var result5 = response5.Parse(); Assert.AreEqual(0, result5.Transfers.Count); - var response6 = await _client.GetBlockTransfers((int)result2.Block.Height); + var response6 = await _client.GetBlockTransfers(result2.Block.Height); var result6 = response6.Parse(); Assert.AreEqual(0, result6.Transfers.Count); var hash1 = await _client.GetStateRootHash(result2.Block.Hash); Assert.AreEqual(32*2, hash1.Length); - var hash2 = await _client.GetStateRootHash((int)result2.Block.Height); + var hash2 = await _client.GetStateRootHash(result2.Block.Height); Assert.AreEqual(hash1, hash2); var hash3 = await _client.GetStateRootHash(); @@ -202,7 +202,7 @@ public async Task GetSystemBlockProposerTest() var result = response.Parse(); Assert.IsNotNull(result.Block.Hash); Assert.IsTrue(result.Block.Proposer.IsSystem); - Assert.AreEqual(result.Block.Proposer.IsSystem, result.Block.Proposer.isSystem); + Assert.AreEqual(result.Block.Proposer.IsSystem, result.Block.Proposer.IsSystem); } catch (RpcClientException e) { @@ -261,7 +261,7 @@ public async Task GetEraSummaryTest() var result3 = response3.Parse(); Assert.IsNotNull(result3.Block.Hash); - var response4 = await _client.GetEraSummary((int)result3.Block.Height); + var response4 = await _client.GetEraSummary(result3.Block.Height); var result4 = response4.Parse(); Assert.IsTrue(result4.EraSummary.EraId > 0); Assert.IsNotNull(result4.EraSummary.StoredValue.EraInfo); diff --git a/Casper.Network.SDK/ICasperClient.cs b/Casper.Network.SDK/ICasperClient.cs index f6ded09..aaa9713 100644 --- a/Casper.Network.SDK/ICasperClient.cs +++ b/Casper.Network.SDK/ICasperClient.cs @@ -10,7 +10,7 @@ public interface ICasperClient { Task GetStateRootHash(string blockHash = null); - Task GetStateRootHash(int blockHeight); + Task GetStateRootHash(ulong blockHeight); Task> GetNodeStatus(); @@ -18,19 +18,19 @@ public interface ICasperClient Task> GetAuctionInfo(string blockHash = null); - Task> GetAuctionInfo(int blockHeight); + Task> GetAuctionInfo(ulong blockHeight); Task> GetAccountInfo(PublicKey publicKey, string blockHash = null); Task> GetAccountInfo(string publicKey, string blockHash = null); - Task> GetAccountInfo(PublicKey publicKey, int blockHeight); + Task> GetAccountInfo(PublicKey publicKey, ulong blockHeight); - Task> GetAccountInfo(string publicKey, int blockHeight); + Task> GetAccountInfo(string publicKey, ulong blockHeight); Task> GetAccountInfo(AccountHashKey accountHash, string blockHash = null); - Task> GetAccountInfo(AccountHashKey accountHash, int blockHeight); + Task> GetAccountInfo(AccountHashKey accountHash, ulong blockHeight); Task> GetEntity(IEntityIdentifier entityIdentifier, string blockHash = null); @@ -40,6 +40,8 @@ public interface ICasperClient Task> GetEntity(string entityAddr, ulong blockHeight); + Task> QueryGlobalState(string key, ulong height, + string path = null); Task> QueryGlobalState(string key, string stateRootHash = null, string path = null); @@ -77,23 +79,37 @@ Task> QueryBalanceDetailsWithStateRootHas Task> GetDeploy(string deployHash, CancellationToken cancellationToken = default(CancellationToken)); + + Task> GetDeploy(string deployHash, + bool finalizedApprovals, + CancellationToken cancellationToken = default(CancellationToken)); + + Task> PutTransaction(TransactionV1 transaction); Task> GetTransaction(TransactionHash transactionHash, bool finalizedApprovals = false, CancellationToken cancellationToken = default(CancellationToken)); + + Task> GetTransaction(string version1Hash, + bool finalizedApprovals = false, + CancellationToken cancellationToken = default(CancellationToken)); Task> GetBlock(string blockHash = null); - Task> GetBlock(int blockHeight); + Task> GetBlock(ulong blockHeight); Task> GetBlockTransfers(string blockHash = null); - Task> GetBlockTransfers(int blockHeight); + Task> GetBlockTransfers(ulong blockHeight); Task> GetEraInfoBySwitchBlock(string blockHash = null); - Task> GetEraInfoBySwitchBlock(int blockHeight); + Task> GetEraInfoBySwitchBlock(ulong blockHeight); + + Task> GetEraSummary(string blockHash = null); + Task> GetEraSummary(ulong blockHeight); + Task> GetDictionaryItem(string dictionaryItem, string stateRootHash = null); Task> GetDictionaryItemByAccount(string accountKey, string dictionaryName, @@ -108,5 +124,11 @@ Task> GetDictionaryItemByURef(string seedUR Task> GetValidatorChanges(); Task GetRpcSchema(); + + Task> GetChainspec(); + + Task> SpeceulativeExecution(Deploy deploy, string stateRootHash = null); + + Task> SpeceulativeExecutionWithBlockHash(Deploy deploy, string blockHash = null); } } diff --git a/Casper.Network.SDK/JsonRpc/CasperMethods.cs b/Casper.Network.SDK/JsonRpc/CasperMethods.cs index dee8ca8..846d822 100644 --- a/Casper.Network.SDK/JsonRpc/CasperMethods.cs +++ b/Casper.Network.SDK/JsonRpc/CasperMethods.cs @@ -20,7 +20,7 @@ public GetStateRootHash(string blockHash = null) : base("chain_get_state_root_ha /// Returns the state root hash at a given Block /// /// Block height for which the state root is queried. - public GetStateRootHash(int height) : base("chain_get_state_root_hash", height) + public GetStateRootHash(ulong height) : base("chain_get_state_root_hash", height) { } } @@ -59,7 +59,7 @@ public GetAuctionInfo(string blockHash) : base("state_get_auction_info", blockHa /// Returns the bids and validators at a given block. /// /// Block height for which the auction info is queried. - public GetAuctionInfo(int height) : base("state_get_auction_info", height) + public GetAuctionInfo(ulong height) : base("state_get_auction_info", height) { } } @@ -81,7 +81,7 @@ public GetAccountInfo(PublicKey publicKey, string blockHash = null) : base("stat /// /// The public key of the account. /// A block height for which the information of the account is queried. - public GetAccountInfo(PublicKey publicKey, int height) : base("state_get_account_info", height) + public GetAccountInfo(PublicKey publicKey, ulong height) : base("state_get_account_info", height) { this.Parameters.Add("account_identifier", publicKey); } @@ -101,7 +101,7 @@ public GetAccountInfo(AccountHashKey accountHash, string blockHash = null) : bas /// /// The account hash of the account. /// A block height for which the information of the account is queried. - public GetAccountInfo(AccountHashKey accountHash, int height) : base("state_get_account_info", height) + public GetAccountInfo(AccountHashKey accountHash, ulong height) : base("state_get_account_info", height) { // this.Parameters.Add("account_identifier", Hex.ToHexString(accountHash.GetBytes())); this.Parameters.Add("account_identifier", accountHash.ToString()); @@ -124,7 +124,7 @@ public GetAccountInfo(string publicKey, string blockHash = null) : base("state_g /// The public key of the account. /// A block height for which the information of the account is queried. [Obsolete("For Casper node v1.5.5 or newer use the new method signature with PublicKey or AccountHashKey", false)] - public GetAccountInfo(string publicKey, int height) : base("state_get_account_info", height) + public GetAccountInfo(string publicKey, ulong height) : base("state_get_account_info", height) { this.Parameters.Add("public_key", publicKey); } @@ -358,7 +358,7 @@ public GetBlock(string blockHash) : base("chain_get_block", blockHash) /// Retrieves a Block from the network by its height number. /// /// Height of the block to retrieve. - public GetBlock(int height) : base("chain_get_block", height) + public GetBlock(ulong height) : base("chain_get_block", height) { } } @@ -377,7 +377,7 @@ public GetBlockTransfers(string blockHash) : base("chain_get_block_transfers", b /// Retrieves all transfers for a Block from the network /// /// Height of the block to retrieve the transfers from. - public GetBlockTransfers(int height) : base("chain_get_block_transfers", height) + public GetBlockTransfers(ulong height) : base("chain_get_block_transfers", height) { } } @@ -396,7 +396,7 @@ public GetEraInfoBySwitchBlock(string blockHash) : base("chain_get_era_info_by_s /// Retrieves an EraInfo from the network given a switch block. /// /// Block height of a switch block. - public GetEraInfoBySwitchBlock(int height) : base("chain_get_era_info_by_switch_block", height) + public GetEraInfoBySwitchBlock(ulong height) : base("chain_get_era_info_by_switch_block", height) { } } @@ -415,7 +415,7 @@ public GetEraSummary(string blockHash) : base("chain_get_era_summary", blockHash /// Retrieves current era info from the network given a block height /// /// Block height. - public GetEraSummary(int height) : base("chain_get_era_summary", height) + public GetEraSummary(ulong height) : base("chain_get_era_summary", height) { } } diff --git a/Casper.Network.SDK/JsonRpc/RpcMethod.cs b/Casper.Network.SDK/JsonRpc/RpcMethod.cs index 3e17c8d..ba99d5b 100644 --- a/Casper.Network.SDK/JsonRpc/RpcMethod.cs +++ b/Casper.Network.SDK/JsonRpc/RpcMethod.cs @@ -76,11 +76,11 @@ public RpcMethod(string method, string blockHash) }; } - public RpcMethod(string method, int blockHeight) + public RpcMethod(string method, ulong blockHeight) { this.Method = method; - var blockIdentifier = new Dictionary + var blockIdentifier = new Dictionary { {"Height", blockHeight} }; diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index b79aa53..0a68507 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -61,7 +61,7 @@ public async Task GetStateRootHash(string blockHash = null) /// /// Block height for which the state root is queried. /// - public async Task GetStateRootHash(int blockHeight) + public async Task GetStateRootHash(ulong blockHeight) { var method = new GetStateRootHash(blockHeight); var rpcResponse = await SendRpcRequestAsync(method); @@ -101,7 +101,7 @@ public async Task> GetAuctionInfo(string block /// Request the bids and validators at a given block. /// /// Block height for which the auction info is queried. - public async Task> GetAuctionInfo(int blockHeight) + public async Task> GetAuctionInfo(ulong blockHeight) { var method = new GetAuctionInfo(blockHeight); return await SendRpcRequestAsync(method); @@ -149,7 +149,7 @@ public async Task> GetAccountInfo(string publi /// /// The public key of the account. /// A block height for which the information of the account is queried. - public async Task> GetAccountInfo(PublicKey publicKey, int blockHeight) + public async Task> GetAccountInfo(PublicKey publicKey, ulong blockHeight) { var method = new GetAccountInfo(publicKey, blockHeight); return await SendRpcRequestAsync(method); @@ -160,7 +160,7 @@ public async Task> GetAccountInfo(PublicKey pu /// /// The account hash of the account. /// A block height for which the information of the account is queried. - public async Task> GetAccountInfo(AccountHashKey accountHash, int blockHeight) + public async Task> GetAccountInfo(AccountHashKey accountHash, ulong blockHeight) { var method = new GetAccountInfo(accountHash, blockHeight); return await SendRpcRequestAsync(method); @@ -172,7 +172,7 @@ public async Task> GetAccountInfo(AccountHashK /// The public key of the account formatted as an hex-string. /// A block height for which the information of the account is queried. [Obsolete("For Casper node v1.5.5 or newer use the new method signature with PublicKey or AccountHashKey, ", false)] - public async Task> GetAccountInfo(string publicKey, int blockHeight) + public async Task> GetAccountInfo(string publicKey, ulong blockHeight) { var method = new GetAccountInfo(publicKey, blockHeight); return await SendRpcRequestAsync(method); @@ -537,7 +537,7 @@ public async Task> GetBlock(string blockHash = null) /// Request a Block from the network by its height number. /// /// Height of the block to retrieve. - public async Task> GetBlock(int blockHeight) + public async Task> GetBlock(ulong blockHeight) { var method = new GetBlock(blockHeight); return await SendRpcRequestAsync(method); @@ -557,7 +557,7 @@ public async Task> GetBlockTransfers(string /// Request all transfers for a Block by its height number. /// /// Height of the block to retrieve the transfers from. - public async Task> GetBlockTransfers(int blockHeight) + public async Task> GetBlockTransfers(ulong blockHeight) { var method = new GetBlockTransfers(blockHeight); return await SendRpcRequestAsync(method); @@ -579,7 +579,7 @@ public async Task> GetEraInfoBySwitch /// For a non-switch block this method returns an empty response. /// /// Block height of a switch block. - public async Task> GetEraInfoBySwitchBlock(int blockHeight) + public async Task> GetEraInfoBySwitchBlock(ulong blockHeight) { var method = new GetEraInfoBySwitchBlock(blockHeight); return await SendRpcRequestAsync(method); @@ -599,7 +599,7 @@ public async Task> GetEraSummary(string blockHa /// Request current Era Info from the network given a block hash /// /// Block height. - public async Task> GetEraSummary(int blockHeight) + public async Task> GetEraSummary(ulong blockHeight) { var method = new GetEraSummary(blockHeight); return await SendRpcRequestAsync(method); From c2674e217decf7bc89de6e16b879b735ea5eaf47 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 12 Jul 2024 13:50:51 +0200 Subject: [PATCH 066/126] Bump up version to 3.0.0-beta1 Signed-off-by: David Hernando --- Casper.Network.SDK/Casper.Network.SDK.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Casper.Network.SDK/Casper.Network.SDK.csproj b/Casper.Network.SDK/Casper.Network.SDK.csproj index 2f98c87..4d3e459 100644 --- a/Casper.Network.SDK/Casper.Network.SDK.csproj +++ b/Casper.Network.SDK/Casper.Network.SDK.csproj @@ -4,9 +4,9 @@ net8.0;netstandard2.0 9.0 CS1591 - 2.3.0.0 - 2.3.0 - 2.3.0 + 3.0.0.0 + 3.0.0-beta1 + 3.0.0-beta1 Casper.Network.SDK make-software https://github.com/make-software/casper-net-sdk From 0d9f7ec8e71e1bf4b58a9119fb44d8d816c0c41d Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 12 Jul 2024 13:53:50 +0200 Subject: [PATCH 067/126] update nuget publish pipeline to .net8 Signed-off-by: David Hernando --- .github/workflows/nuget-publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nuget-publish.yml b/.github/workflows/nuget-publish.yml index 41533ec..c02101f 100644 --- a/.github/workflows/nuget-publish.yml +++ b/.github/workflows/nuget-publish.yml @@ -14,7 +14,7 @@ jobs: - name: Setup dotnet uses: actions/setup-dotnet@v1 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: publish on version change id: publish_nuget From e3a2fa48302ffad40fe73ba0239d9bf7376cc35d Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 12 Jul 2024 19:17:15 +0200 Subject: [PATCH 068/126] Fixed typos in migration guide --- Docs/Articles/CondorMigrationGuide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Docs/Articles/CondorMigrationGuide.md b/Docs/Articles/CondorMigrationGuide.md index b136a39..0c7cc7e 100644 --- a/Docs/Articles/CondorMigrationGuide.md +++ b/Docs/Articles/CondorMigrationGuide.md @@ -6,7 +6,7 @@ This guide outlines the changes necessary for your application. However, it's wo ## Blocks -With Condor, produced blocks are stored in a new format that extends the information contained compared to old blocks. Blocks produced before the upgrade keep their original format. Thus, the SDK implements `BlockV1` and `BlocV2` classes to handle old and new block formats, respectively. +With Condor, produced blocks are stored in a new format that extends the information contained compared to old blocks. Blocks produced before the upgrade keep their original format. Thus, the SDK implements `BlockV1` and `BlockV2` classes to handle old and new block formats, respectively. To facilitate handling different versions, the SDK also implements the type `Block`, which can represent either a V1 or V2 type in the network. This is the type obtained by default in the RPC queries and the SSE channel and contains all the data you may want to query: @@ -34,7 +34,7 @@ public class Block Note that `Block` does not have a header or body parts. -Also, some properties may have a `null` value if they’re not part of the versioned block. For example, `LastSwitchBlockHas` is present only for V2 blocks and `null` for V1 blocks. +Also, some properties may have a `null` value if they’re not part of the versioned block. For example, `LastSwitchBlockHash` is present only for V2 blocks and `null` for V1 blocks. ### Recovering the versioned block object From f42bb6e46c8bf50967366679a8e8b4aeb1047c78 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 15 Jul 2024 11:56:22 +0200 Subject: [PATCH 069/126] Update push-to-github-registry.yml --- .github/workflows/push-to-github-registry.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/push-to-github-registry.yml b/.github/workflows/push-to-github-registry.yml index fa33398..3a1564f 100644 --- a/.github/workflows/push-to-github-registry.yml +++ b/.github/workflows/push-to-github-registry.yml @@ -13,7 +13,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v1 with: - dotnet-version: 7.0.x + dotnet-version: 8.0.x - name: Create nuget package run: dotnet pack --configuration Release -o out - name: Publish Nuget to GitHub registry From 2287749da49936538c07d13604216d59550cf99c Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 16 Jul 2024 09:28:40 +0200 Subject: [PATCH 070/126] CSDK-187 implemented info-get-reward RPC method Signed-off-by: David Hernando --- .../RPCResponses/GetRewardResultTest.cs | 23 +++++ .../TestData/info-get-reward-v200.json | 1 + Casper.Network.SDK/JsonRpc/CasperMethods.cs | 84 +++++++++++++++++++ .../JsonRpc/ResultTypes/GetRewardResult.cs | 31 +++++++ Casper.Network.SDK/NetCasperClient.cs | 69 +++++++++++++++ 5 files changed, 208 insertions(+) create mode 100644 Casper.Network.SDK.Test/RPCResponses/GetRewardResultTest.cs create mode 100644 Casper.Network.SDK.Test/TestData/info-get-reward-v200.json create mode 100644 Casper.Network.SDK/JsonRpc/ResultTypes/GetRewardResult.cs diff --git a/Casper.Network.SDK.Test/RPCResponses/GetRewardResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetRewardResultTest.cs new file mode 100644 index 0000000..6a5cddb --- /dev/null +++ b/Casper.Network.SDK.Test/RPCResponses/GetRewardResultTest.cs @@ -0,0 +1,23 @@ +using System.IO; +using Casper.Network.SDK.JsonRpc.ResultTypes; +using NUnit.Framework; + +namespace NetCasperTest.RPCResponses +{ + public class GetRewardResultTest + { + [Test] + public void GetBlockResultTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/info-get-reward-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + Assert.AreEqual(13, result.EraId); + Assert.AreEqual(1, result.DelegationRate); + Assert.AreEqual("62559062048560", result.Amount.ToString()); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/info-get-reward-v200.json b/Casper.Network.SDK.Test/TestData/info-get-reward-v200.json new file mode 100644 index 0000000..4565644 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/info-get-reward-v200.json @@ -0,0 +1 @@ +{"api_version":"2.0.0","reward_amount":"62559062048560","era_id":13,"delegation_rate":1} diff --git a/Casper.Network.SDK/JsonRpc/CasperMethods.cs b/Casper.Network.SDK/JsonRpc/CasperMethods.cs index 846d822..2c6954e 100644 --- a/Casper.Network.SDK/JsonRpc/CasperMethods.cs +++ b/Casper.Network.SDK/JsonRpc/CasperMethods.cs @@ -546,6 +546,90 @@ public GetValidatorChanges() : base("info_get_validator_changes") } } + public class GetValidatorReward : RpcMethod + { + /// + /// Returns the reward for a given era and a validator + /// + /// The public key of the validator. + /// The identifier for the state used for the query, if none is passed, the latest block will be used. + public GetValidatorReward(PublicKey validator, IBlockIdentifier blockIdentifier = null) : base("info_get_reward") + { + this.Parameters = new Dictionary + { + { "validator", validator.ToString() } + }; + + if (blockIdentifier != null) + this.Parameters.Add("era_identifier", new Dictionary + { + { "Block", blockIdentifier.GetBlockIdentifier() } + }); + } + + /// + /// Returns the reward for a given era and a validator + /// + /// The public key of the validator. + /// Id of the Era to retrieve the rewards from. + public GetValidatorReward(PublicKey validator, ulong eraId ) : base("info_get_reward") + { + this.Parameters = new Dictionary + { + { "validator", validator.ToString() }, + { "era_identifier", new Dictionary + { + { "Era", eraId } + } + } + }; + } + } + + public class GetDelegatorReward : RpcMethod + { + /// + /// Returns the reward for a given era and a delegator + /// + /// The public key of the validator. + /// The public key of the delegator. + /// The identifier for the state used for the query, if none is passed, the latest block will be used. + public GetDelegatorReward(PublicKey validator, PublicKey delegator, IBlockIdentifier blockIdentifier = null) : base("info_get_reward") + { + this.Parameters = new Dictionary + { + { "validator", validator.ToString() }, + { "delegator", delegator.ToString() } + }; + + if (blockIdentifier != null) + this.Parameters.Add("era_identifier", new Dictionary + { + { "Block", blockIdentifier.GetBlockIdentifier() } + }); + } + + /// + /// Returns the reward for a given era and a delegator + /// + /// The public key of the validator. + /// The public key of the delegator. + /// Id of the Era to retrieve the rewards from. + public GetDelegatorReward(PublicKey validator, PublicKey delegator, ulong eraId ) : base("info_get_reward") + { + this.Parameters = new Dictionary + { + { "validator", validator.ToString() }, + { "delegator", delegator.ToString() }, + { "era_identifier", new Dictionary + { + { "Era", eraId } + } + } + }; + } + } + public class GetRpcSchema : RpcMethod { /// diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetRewardResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetRewardResult.cs new file mode 100644 index 0000000..0106c6a --- /dev/null +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetRewardResult.cs @@ -0,0 +1,31 @@ +using System.Numerics; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; + +namespace Casper.Network.SDK.JsonRpc.ResultTypes +{ + /// + /// Result for \"info_get_reward\" RPC response. + /// + public class GetRewardResult : RpcResult + { + /// + /// The era for which the reward was calculated. + /// + [JsonPropertyName("era_id")] + public ulong EraId { get; init; } + + /// + /// The reward amount. + /// + [JsonPropertyName("reward_amount")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Amount { get; init; } + + /// + /// The delegation rate of the validator. + /// + [JsonPropertyName("delegation_rate")] + public uint DelegationRate { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index 0a68507..4abb580 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -678,6 +678,75 @@ public async Task> GetValidatorChanges() return await SendRpcRequestAsync(method); } + /// + /// Returns the reward for a given era and a validator + /// + /// The public key of the validator. + /// Hash of the block to retrieve the rewards from. Null for the most recent era + public async Task> GetValidatorReward(PublicKey validator, string blockHash = null) + { + var method = new GetValidatorReward(validator, blockHash != null ? new BlockIdentifier(blockHash) : null); + return await SendRpcRequestAsync(method); + } + + /// + /// Returns the reward for a given era and a validator + /// + /// The public key of the validator. + /// Height of the block to retrieve the rewards from. + public async Task> GetValidatorReward(PublicKey validator, ulong blockHeight) + { + var method = new GetValidatorReward(validator, new BlockIdentifier(blockHeight)); + return await SendRpcRequestAsync(method); + } + + /// + /// Returns the reward for a given era and a validator + /// + /// The public key of the validator. + /// Id of the Era to retrieve the rewards from. + public async Task> GetValidatorRewardWithEraId(PublicKey validator, ulong eraId) + { + var method = new GetValidatorReward(validator, eraId); + return await SendRpcRequestAsync(method); + } + + /// + /// Returns the reward for a given era and a delegator + /// + /// The public key of the validator. + /// The public key of the delegator. + /// Hash of the block to retrieve the rewards from. Null for the most recent era + public async Task> GetDelegatorReward(PublicKey validator, PublicKey delegator, string blockHash = null) + { + var method = new GetDelegatorReward(validator, delegator, blockHash != null ? new BlockIdentifier(blockHash) : null); + return await SendRpcRequestAsync(method); + } + + /// + /// Returns the reward for a given era and a delegator + /// + /// The public key of the validator. + /// The public key of the delegator. + /// Height of the block to retrieve the rewards from. + public async Task> GetDelegatorReward(PublicKey validator, PublicKey delegator, ulong blockHeight) + { + var method = new GetDelegatorReward(validator, delegator, new BlockIdentifier(blockHeight)); + return await SendRpcRequestAsync(method); + } + + /// + /// Returns the reward for a given era and a delegator + /// + /// The public key of the validator. + /// The public key of the delegator. + /// Id of the Era to retrieve the rewards from. + public async Task> GetDelegatorRewardWithEraId(PublicKey validator, PublicKey delegator, ulong eraId) + { + var method = new GetDelegatorReward(validator, delegator, eraId); + return await SendRpcRequestAsync(method); + } + /// /// Request the RPC Json schema to the network node. /// From 54ed8292087c658e61c98c40117640d4e680a362 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 16 Jul 2024 10:04:27 +0200 Subject: [PATCH 071/126] CSDK-189 added protocol_version to get node status response Signed-off-by: David Hernando --- .../RPCResponses/GetNodeStatusResultTest.cs | 3 ++- Casper.Network.SDK.Test/TestData/info-get-status-v200.json | 3 ++- .../JsonRpc/ResultTypes/GetNodeStatusResult.cs | 6 ++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Casper.Network.SDK.Test/RPCResponses/GetNodeStatusResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetNodeStatusResultTest.cs index c079bad..bbe2f32 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetNodeStatusResultTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetNodeStatusResultTest.cs @@ -16,7 +16,8 @@ public void GetBlockResultTest_v200() var result = RpcResult.Parse(json); Assert.IsNotNull(result); - Assert.AreEqual("2.0.0", result.ApiVersion); + Assert.AreEqual("1.2.3", result.ApiVersion); + Assert.AreEqual("5.4.3", result.ProtocolVersion); Assert.AreEqual(4, result.Peers.Count); Assert.IsNotNull(result.Peers[1].NodeId); Assert.IsNotNull(result.Peers[1].Address); diff --git a/Casper.Network.SDK.Test/TestData/info-get-status-v200.json b/Casper.Network.SDK.Test/TestData/info-get-status-v200.json index da612fe..fdc26ab 100644 --- a/Casper.Network.SDK.Test/TestData/info-get-status-v200.json +++ b/Casper.Network.SDK.Test/TestData/info-get-status-v200.json @@ -1,5 +1,6 @@ { - "api_version": "2.0.0", + "api_version": "1.2.3", + "protocol_version": "5.4.3", "peers": [ { "node_id": "tls:0eae..e79e", diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetNodeStatusResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetNodeStatusResult.cs index 1213eab..aa56686 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetNodeStatusResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetNodeStatusResult.cs @@ -85,5 +85,11 @@ public class GetNodeStatusResult : RpcResult /// The hash of the latest switch block. /// [JsonPropertyName("latest_switch_block_hash")] public string LatestSwitchBlockHash { get; init; } + + /// + /// The protocol version running in the node + /// + [JsonPropertyName("protocol_version")] + public string ProtocolVersion { get; init; } } } From 967d689710d4131e2d70758ca7c1e43eab099495 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 18 Jul 2024 11:01:05 +0200 Subject: [PATCH 072/126] Implement Transaction class as an abstraction of Deploy and TransactionV1. Refactor TransactionV1 to use interfaces instead of multiple purposes classes. WIP (pending refactoring of PricingMode) Signed-off-by: David Hernando --- .../RPCResponses/GetTransactionTest.cs | 180 ++ .../TestData/get-deploy-deploy-v200.json | 197 +++ .../get-transaction-deploy-stored-v200.json | 282 ++++ .../TestData/get-transaction-deploy-v200.json | 199 +++ .../get-transaction-session-v200.json | 1469 +++++++++++++++++ .../TestData/get-transaction-stored-v200.json | 279 ++++ .../TransactionV1ByteSerializer.cs | 162 +- Casper.Network.SDK/ICasperClient.cs | 8 +- Casper.Network.SDK/JsonRpc/CasperMethods.cs | 39 +- .../ResultTypes/GetTransactionResult.cs | 1 + Casper.Network.SDK/NetCasperClient.cs | 56 +- Casper.Network.SDK/SSE/TransactionAccepted.cs | 1 + Casper.Network.SDK/Types/Block.cs | 26 +- Casper.Network.SDK/Types/InitiatorAddr.cs | 10 + Casper.Network.SDK/Types/Package.cs | 2 +- Casper.Network.SDK/Types/PricingMode.cs | 70 +- Casper.Network.SDK/Types/Transaction.cs | 346 +++- .../Types/TransactionEntryPoint.cs | 116 -- .../Types/TransactionScheduling.cs | 105 -- Casper.Network.SDK/Types/TransactionV1Body.cs | 18 +- .../Types/TransactionV1EntryPoint.cs | 184 +++ .../Types/TransactionV1Header.cs | 6 + .../Types/TransactionV1Scheduling.cs | 149 ++ ...actionTarget.cs => TransactionV1Target.cs} | 124 +- .../Utils/RpcResponseExtensions.cs | 22 + 25 files changed, 3642 insertions(+), 409 deletions(-) create mode 100644 Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs create mode 100644 Casper.Network.SDK.Test/TestData/get-deploy-deploy-v200.json create mode 100644 Casper.Network.SDK.Test/TestData/get-transaction-deploy-stored-v200.json create mode 100644 Casper.Network.SDK.Test/TestData/get-transaction-deploy-v200.json create mode 100644 Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json create mode 100644 Casper.Network.SDK.Test/TestData/get-transaction-stored-v200.json delete mode 100644 Casper.Network.SDK/Types/TransactionEntryPoint.cs delete mode 100644 Casper.Network.SDK/Types/TransactionScheduling.cs create mode 100644 Casper.Network.SDK/Types/TransactionV1EntryPoint.cs create mode 100644 Casper.Network.SDK/Types/TransactionV1Scheduling.cs rename Casper.Network.SDK/Types/{TransactionTarget.cs => TransactionV1Target.cs} (74%) diff --git a/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs new file mode 100644 index 0000000..c6124ec --- /dev/null +++ b/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs @@ -0,0 +1,180 @@ +using System.IO; +using Casper.Network.SDK.JsonRpc.ResultTypes; +using Casper.Network.SDK.Types; +using NUnit.Framework; +using Org.BouncyCastle.Utilities.Encoders; + +namespace NetCasperTest.RPCResponses +{ + public class GetTransactionTest + { + [Test] + public void GetTransactionWithDeployHashTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-transaction-deploy-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + + Assert.IsNotNull(result.Transaction); + var deploy = (Deploy)result.Transaction; + Assert.NotNull(deploy); + Assert.AreEqual(deploy.Hash, result.Transaction.Hash); + Assert.AreEqual(deploy.Header.ChainName, result.Transaction.ChainName); + Assert.AreEqual(deploy.Header.Timestamp, result.Transaction.Timestamp); + Assert.AreEqual(deploy.Header.Account, result.Transaction.InitiatorAddr.PublicKey); + Assert.AreEqual("c5cf594dc6d244358fddd6461e93703653bb07a0d9fa957c4a4b5fdc5bc9968d", deploy.Header.BodyHash); + Assert.IsNull(result.Transaction.InitiatorAddr.AccountHash); + Assert.IsTrue(result.Transaction.Invocation is Transaction.NativeTransactionInvocation); + Assert.AreEqual(NativeEntryPoint.Transfer, (result.Transaction.Invocation as Transaction.NativeTransactionInvocation)!.Type); + Assert.AreEqual(TransactionCategory.Mint, result.Transaction.Category); + Assert.IsTrue(result.Transaction.Scheduling is StandardTransactionScheduling); + Assert.AreEqual(deploy.Approvals.Count, result.Transaction.Approvals.Count); + Assert.AreEqual(deploy.Approvals[0].Signature, result.Transaction.Approvals[0].Signature); + + AssertExtensions.IsHash(result.ExecutionInfo.BlockHash); + Assert.IsTrue(result.ExecutionInfo.BlockHeight > 0); + Assert.IsNotNull(result.ExecutionInfo.ExecutionResult); + } + + [Test] + public void GetDeployWithDeployHashTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-deploy-deploy-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + + var deploy = result.Deploy; + Assert.NotNull(deploy); + + var transaction = (Transaction)deploy; + Assert.IsNotNull(transaction); + + Assert.AreEqual(deploy.Hash, transaction.Hash); + Assert.AreEqual(deploy.Header.ChainName, transaction.ChainName); + Assert.AreEqual(deploy.Header.Timestamp, transaction.Timestamp); + Assert.AreEqual(deploy.Header.Account, transaction.InitiatorAddr.PublicKey); + Assert.AreEqual("c5cf594dc6d244358fddd6461e93703653bb07a0d9fa957c4a4b5fdc5bc9968d", deploy.Header.BodyHash); + Assert.IsNull(transaction.InitiatorAddr.AccountHash); + Assert.IsTrue(transaction.Invocation is Transaction.NativeTransactionInvocation); + Assert.AreEqual(NativeEntryPoint.Transfer, (transaction.Invocation as Transaction.NativeTransactionInvocation)!.Type); + Assert.AreEqual(TransactionCategory.Mint, transaction.Category); + Assert.IsTrue(transaction.Scheduling is StandardTransactionScheduling); + Assert.AreEqual(deploy.Approvals.Count, transaction.Approvals.Count); + Assert.AreEqual(deploy.Approvals[0].Signature, transaction.Approvals[0].Signature); + + AssertExtensions.IsHash(result.ExecutionInfo.BlockHash); + Assert.IsTrue(result.ExecutionInfo.BlockHeight > 0); + Assert.IsNotNull(result.ExecutionInfo.ExecutionResult); + } + + [Test] + public void GetTransactionWithSessionTransactionTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-transaction-session-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + + var transaction = result.Transaction; + Assert.NotNull(transaction); + + var transactionV1 = (TransactionV1)transaction; + Assert.IsNotNull(transactionV1); + + Assert.AreEqual(transactionV1.Hash, transaction.Hash); + Assert.AreEqual("9b1d4002bb1af17ec9fc4193ac5ba7db6535b6dd813034b730829a94b13ebc46", transactionV1.Header.BodyHash); + Assert.AreEqual(transactionV1.Header.ChainName, transaction.ChainName); + Assert.AreEqual(transactionV1.Header.Timestamp, transaction.Timestamp); + Assert.AreEqual(transactionV1.Header.InitiatorAddr.PublicKey, transaction.InitiatorAddr.PublicKey); + Assert.IsNull(transaction.InitiatorAddr.AccountHash); + Assert.IsTrue(transaction.Invocation is Transaction.SessionTransactionInvocation); + Assert.AreEqual("01020304", Hex.ToHexString((transaction.Invocation as Transaction.SessionTransactionInvocation)!.Wasm)); + Assert.AreEqual(TransactionCategory.InstallUpgrade, transaction.Category); + Assert.IsTrue(transaction.Scheduling is StandardTransactionScheduling); + Assert.AreEqual(transactionV1.Approvals.Count, transaction.Approvals.Count); + Assert.AreEqual(transactionV1.Approvals[0].Signature, transaction.Approvals[0].Signature); + + AssertExtensions.IsHash(result.ExecutionInfo.BlockHash); + Assert.IsTrue(result.ExecutionInfo.BlockHeight > 0); + Assert.IsNotNull(result.ExecutionInfo.ExecutionResult); + } + + [Test] + public void GetTransactionWithStoredTransactionTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-transaction-stored-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + + var transaction = result.Transaction; + Assert.NotNull(transaction); + + var transactionV1 = (TransactionV1)transaction; + Assert.IsNotNull(transactionV1); + + Assert.AreEqual(transactionV1.Hash, transaction.Hash); + Assert.AreEqual("07ed52f990a206153d2a29f6a42eb754009c4b120981dd3fd0842d4057ee7484", transactionV1.Header.BodyHash); + Assert.AreEqual(transactionV1.Header.ChainName, transaction.ChainName); + Assert.AreEqual(transactionV1.Header.Timestamp, transaction.Timestamp); + Assert.AreEqual(transactionV1.Header.InitiatorAddr.AccountHash, transaction.InitiatorAddr.AccountHash); + Assert.AreEqual("account-hash-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f", transaction.InitiatorAddr.AccountHash.ToString().ToLower()); + Assert.IsNull(transaction.InitiatorAddr.PublicKey); + Assert.IsTrue(transaction.Invocation is Transaction.StoredTransactionInvocation); + Assert.AreEqual("transfer", (transaction.Invocation as Transaction.StoredTransactionInvocation)!.EntryPoint); + Assert.AreEqual(TransactionCategory.Medium, transaction.Category); + Assert.IsTrue(transaction.Scheduling is StandardTransactionScheduling); + Assert.AreEqual(transactionV1.Approvals.Count, transaction.Approvals.Count); + Assert.AreEqual(transactionV1.Approvals[0].Signature, transaction.Approvals[0].Signature); + + AssertExtensions.IsHash(result.ExecutionInfo.BlockHash); + Assert.IsTrue(result.ExecutionInfo.BlockHeight > 0); + Assert.IsNotNull(result.ExecutionInfo.ExecutionResult); + } + + [Test] + public void GetTransactionWithStoredDeployTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-transaction-deploy-stored-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + + var transaction = result.Transaction; + Assert.NotNull(transaction); + + var deploy = (Deploy)transaction; + Assert.IsNotNull(deploy); + + Assert.AreEqual(deploy.Hash, transaction.Hash); + Assert.AreEqual("8d18c7a727317c54fb8ac21ba2e02ec8f4d24d0f4cb4183d3df05afe86ba614c", deploy.Header.BodyHash); + Assert.AreEqual(deploy.Header.ChainName, transaction.ChainName); + Assert.AreEqual(deploy.Header.Timestamp, transaction.Timestamp); + Assert.AreEqual(deploy.Header.Account, transaction.InitiatorAddr.PublicKey); + Assert.AreEqual("0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", transaction.InitiatorAddr.PublicKey.ToString().ToLower()); + Assert.IsNull(transaction.InitiatorAddr.AccountHash); + Assert.IsTrue(transaction.Invocation is Transaction.StoredTransactionInvocation); + Assert.AreEqual("transfer", (transaction.Invocation as Transaction.StoredTransactionInvocation)!.EntryPoint); + Assert.AreEqual(TransactionCategory.Large, transaction.Category); + Assert.IsTrue(transaction.Scheduling is StandardTransactionScheduling); + Assert.AreEqual(deploy.Approvals.Count, transaction.Approvals.Count); + Assert.AreEqual(deploy.Approvals[0].Signature, transaction.Approvals[0].Signature); + + AssertExtensions.IsHash(result.ExecutionInfo.BlockHash); + Assert.IsTrue(result.ExecutionInfo.BlockHeight > 0); + Assert.IsNotNull(result.ExecutionInfo.ExecutionResult); + } + } +} diff --git a/Casper.Network.SDK.Test/TestData/get-deploy-deploy-v200.json b/Casper.Network.SDK.Test/TestData/get-deploy-deploy-v200.json new file mode 100644 index 0000000..058c77e --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-deploy-deploy-v200.json @@ -0,0 +1,197 @@ +{ + "api_version": "2.0.0", + "deploy": { + "hash": "53363985887173c70955bee232e280429f53ab208e2397b19a5e761215734e56", + "header": { + "account": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", + "timestamp": "2024-07-17T07:00:19.247Z", + "ttl": "30m", + "gas_price": 1, + "body_hash": "c5cf594dc6d244358fddd6461e93703653bb07a0d9fa957c4a4b5fdc5bc9968d", + "dependencies": [], + "chain_name": "casper-net-1" + }, + "payment": { + "ModuleBytes": { + "module_bytes": "", + "args": [ + [ + "amount", + { + "cl_type": "U512", + "bytes": "0100", + "parsed": "0" + } + ] + ] + } + }, + "session": { + "Transfer": { + "args": [ + [ + "amount", + { + "cl_type": "U512", + "bytes": "0500ba1dd205", + "parsed": "25000000000" + } + ], + [ + "target", + { + "cl_type": { + "ByteArray": 32 + }, + "bytes": "6bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a", + "parsed": "6bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a" + } + ], + [ + "id", + { + "cl_type": { + "Option": "U64" + }, + "bytes": "010100000000000000", + "parsed": 1 + } + ] + ] + } + }, + "approvals": [ + { + "signer": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", + "signature": "01a42c794b4f9f1d344a41ec67f0a01a3f5c18b7a04c8948118d172993137d9db526c0e2512b89c45fc100d6cab9c562cccc295d6b0fd667a0c9d211cae88c570b" + } + ] + }, + "execution_info": { + "block_hash": "4ab00f303c62fe665fabf1112fc08b995c31b62b7af13390b417d8ba483597d4", + "block_height": 74, + "execution_result": { + "Version2": { + "initiator": { + "PublicKey": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a" + }, + "error_message": null, + "limit": "10000", + "consumed": "10000", + "cost": "10000", + "payment": [], + "transfers": [ + { + "Version2": { + "transaction_hash": { + "Deploy": "53363985887173c70955bee232e280429f53ab208e2397b19a5e761215734e56" + }, + "from": { + "AccountHash": "account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4" + }, + "to": "account-hash-6bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a", + "source": "uref-8b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce85142-007", + "target": "uref-98735896568d7b44005778ed909e83457e8923c76b2a23bcec09643cc384cfce-004", + "amount": "25000000000", + "gas": "0", + "id": 1 + } + } + ], + "size_estimate": 367, + "effects": [ + { + "key": "balance-hold-018b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce85142d0f57dbf90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "021027", + "parsed": "10000" + } + } + } + }, + { + "key": "account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4", + "kind": "Identity" + }, + { + "key": "balance-hold-008b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce8514256207dbf90010000", + "kind": "Identity" + }, + { + "key": "balance-hold-018b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce85142d0f57dbf90010000", + "kind": "Identity" + }, + { + "key": "balance-8b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce85142", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "0e005e6be5ec5ac138938d44c64d31", + "parsed": "999999999999999999999875000000000" + } + } + } + }, + { + "key": "balance-98735896568d7b44005778ed909e83457e8923c76b2a23bcec09643cc384cfce", + "kind": { + "AddUInt512": "25000000000" + } + }, + { + "key": "balance-hold-018b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce85142d0f57dbf90010000", + "kind": { + "Prune": "balance-hold-018b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce85142d0f57dbf90010000" + } + }, + { + "key": "balance-hold-008b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce85142d0f57dbf90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "021027", + "parsed": "10000" + } + } + } + }, + { + "key": "entity-system-8499396a854bbe7e172a5ab675caf3c7b944f57e43cee5b7563971678f8650b2", + "kind": "Identity" + }, + { + "key": "entity-system-989625d28e779749e3cacc7367403dd459c8e7082937436f16d5dbff7aec8292", + "kind": "Identity" + }, + { + "key": "entity-system-da2faf76ab117ec370d10f994e9407ff014ab974bedc50e58425a9e35428dd0c", + "kind": "Identity" + }, + { + "key": "bid-addr-01dcd0c38c46c9d5c7083aa1a46b430e8e460f97f8f0bf8444776ac925187acfcc", + "kind": "Identity" + }, + { + "key": "bid-addr-04dcd0c38c46c9d5c7083aa1a46b430e8e460f97f8f0bf8444776ac925187acfcc0900000000000000", + "kind": { + "Write": { + "BidKind": { + "Credit": { + "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "era_id": 9, + "amount": "10000" + } + } + } + } + } + ] + } + } + } +} diff --git a/Casper.Network.SDK.Test/TestData/get-transaction-deploy-stored-v200.json b/Casper.Network.SDK.Test/TestData/get-transaction-deploy-stored-v200.json new file mode 100644 index 0000000..54766a3 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-transaction-deploy-stored-v200.json @@ -0,0 +1,282 @@ +{ + "api_version": "2.0.0", + "transaction": { + "Deploy": { + "hash": "7958e3156a2770aec31587d61bcbc92073492d6d4fc8c99a7fd5718e534dee31", + "header": { + "account": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", + "timestamp": "2024-07-17T09:12:04.578Z", + "ttl": "3m", + "gas_price": 1, + "body_hash": "8d18c7a727317c54fb8ac21ba2e02ec8f4d24d0f4cb4183d3df05afe86ba614c", + "dependencies": [], + "chain_name": "casper-net-1" + }, + "payment": { + "ModuleBytes": { + "module_bytes": "", + "args": [ + [ + "amount", + { + "cl_type": "U512", + "bytes": "05001417c668", + "parsed": "450000000000" + } + ] + ] + } + }, + "session": { + "StoredVersionedContractByName": { + "name": "cep18_contract_package_CLICK1 Test", + "version": null, + "entry_point": "transfer", + "args": [ + [ + "recipient", + { + "cl_type": "Key", + "bytes": "00ffaa73331421de42fd71a28824e1606a1ca7fa754f91f501f0cf56015f7284cd", + "parsed": "account-hash-ffaa73331421de42fd71a28824e1606a1ca7fa754f91f501f0cf56015f7284cd" + } + ], + [ + "amount", + { + "cl_type": "U256", + "bytes": "02d430", + "parsed": "12500" + } + ] + ] + } + }, + "approvals": [ + { + "signer": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", + "signature": "01bcc798d081a34bdce01167b4b37a713f13da89eaf4e7382c6757646cb36da97dfa43c60498860ecbabaf2c68867b459f625b6006bec12b84ab0ea94c5a892a00" + } + ] + } + }, + "execution_info": { + "block_hash": "827d030f98782c1e68da7f6be464681821ab6a49c134364ac1b3fdbed236b25f", + "block_height": 2123, + "execution_result": { + "Version2": { + "initiator": { + "PublicKey": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4" + }, + "error_message": null, + "limit": "500000000000", + "consumed": "760483530", + "cost": "500000000000", + "payment": [], + "transfers": [], + "size_estimate": 398, + "effects": [ + { + "key": "balance-hold-016f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee0422155198f6bf90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "050088526a74", + "parsed": "500000000000" + } + } + } + }, + { + "key": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", + "kind": "Identity" + }, + { + "key": "entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58", + "kind": "Identity" + }, + { + "key": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", + "kind": "Identity" + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-3820ce25e54df0470fb738e3e0f63ee50b2719cf2680da2bdb579e21aebc8f63", + "kind": "Identity" + }, + { + "key": "byte-code-v1-wasm-800e35cfd87b0f36479afdaf68725cd7ef0f503e8d541ed33afad4a18c7fa6eb", + "kind": "Identity" + }, + { + "key": "dictionary-ede600b099900d357796a3e23eecb1e5277bdfb93297c36f0c039d2c3a5c6557", + "kind": "Identity" + }, + { + "key": "dictionary-ede600b099900d357796a3e23eecb1e5277bdfb93297c36f0c039d2c3a5c6557", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "0800000007162ac5a47e8d030720000000f032be9ae9bd295ec0403aa886d7247d27c296751176bd88d3b02c996626fd053000000045514869587777376d477171476d7946376a5672365a7a5449506f66664f72356b6f6f2f765146647352386b44773d3d", + "parsed": null + } + } + } + }, + { + "key": "dictionary-7a57f65f11b9d64d61137b5ad8212378b357078550a2104fdecd6e355c26e052", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "0300000002d4300720000000f032be9ae9bd295ec0403aa886d7247d27c296751176bd88d3b02c996626fd052c00000041502b71637a4d55496435432f5847696943546859476f63702f7031543548314166445056674666636f544e", + "parsed": null + } + } + } + }, + { + "key": "uref-08f9c3e110c4d5cbc6e8ce71dd0cd535e4f41b34fe88c26cf3cd222004373db3-000", + "kind": "Identity" + }, + { + "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "kind": "Identity" + }, + { + "key": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-1", + "kind": { + "Prune": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-1" + } + }, + { + "key": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-2", + "kind": { + "Prune": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-2" + } + }, + { + "key": "block-message-count-00000000000000000000000000000000000000000000000000000000000000", + "kind": "Identity" + }, + { + "key": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-0", + "kind": { + "Write": { + "Message": "message-checksum-4e8fa8422d6022df02a2d41568e25ebfbc9d7d098f8c31f42e4493523627f0f4" + } + } + }, + { + "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "kind": { + "Write": { + "MessageTopic": { + "message_count": 1, + "blocktime": 1721207527505 + } + } + } + }, + { + "key": "block-message-count-00000000000000000000000000000000000000000000000000000000000000", + "kind": { + "Write": { + "CLValue": { + "cl_type": { + "Tuple2": [ + "U64", + "U64" + ] + }, + "bytes": "5198f6bf900100000100000000000000", + "parsed": [ + 1721207527505, + 1 + ] + } + } + } + }, + { + "key": "uref-ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae4-000", + "kind": "Identity" + }, + { + "key": "dictionary-d16f0a36780c0a7f72457a751a5f79125b1a3c492cb86456adeff5b0111d2640", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "5c000000580000000e0000006576656e745f5472616e736665721101e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f00ffaa73331421de42fd71a28824e1606a1ca7fa754f91f501f0cf56015f7284cd02d4300e03200000009d1bc561e4c28250e3595b12b78989409f69ad336a8e398b4e9f6b91b3c283170100000037", + "parsed": null + } + } + } + }, + { + "key": "uref-ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae4-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U32", + "bytes": "08000000", + "parsed": 8 + } + } + } + }, + { + "key": "balance-hold-016f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee0422155198f6bf90010000", + "kind": { + "Prune": "balance-hold-016f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee0422155198f6bf90010000" + } + }, + { + "key": "balance-hold-006f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee0422155198f6bf90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "050088526a74", + "parsed": "500000000000" + } + } + } + }, + { + "key": "entity-system-8499396a854bbe7e172a5ab675caf3c7b944f57e43cee5b7563971678f8650b2", + "kind": "Identity" + }, + { + "key": "entity-system-989625d28e779749e3cacc7367403dd459c8e7082937436f16d5dbff7aec8292", + "kind": "Identity" + }, + { + "key": "entity-system-da2faf76ab117ec370d10f994e9407ff014ab974bedc50e58425a9e35428dd0c", + "kind": "Identity" + }, + { + "key": "bid-addr-011c50d14ca563d5afe0399a3da010bac01383f502bc7cce774cc03d858a94cf06", + "kind": "Identity" + }, + { + "key": "bid-addr-041c50d14ca563d5afe0399a3da010bac01383f502bc7cce774cc03d858a94cf06c400000000000000", + "kind": { + "Write": { + "BidKind": { + "Credit": { + "validator_public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "era_id": 196, + "amount": "500000000000" + } + } + } + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/get-transaction-deploy-v200.json b/Casper.Network.SDK.Test/TestData/get-transaction-deploy-v200.json new file mode 100644 index 0000000..05ef8fc --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-transaction-deploy-v200.json @@ -0,0 +1,199 @@ +{ + "api_version": "2.0.0", + "transaction": { + "Deploy": { + "hash": "53363985887173c70955bee232e280429f53ab208e2397b19a5e761215734e56", + "header": { + "account": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", + "timestamp": "2024-07-17T07:00:19.247Z", + "ttl": "30m", + "gas_price": 1, + "body_hash": "c5cf594dc6d244358fddd6461e93703653bb07a0d9fa957c4a4b5fdc5bc9968d", + "dependencies": [], + "chain_name": "casper-net-1" + }, + "payment": { + "ModuleBytes": { + "module_bytes": "", + "args": [ + [ + "amount", + { + "cl_type": "U512", + "bytes": "0100", + "parsed": "0" + } + ] + ] + } + }, + "session": { + "Transfer": { + "args": [ + [ + "amount", + { + "cl_type": "U512", + "bytes": "0500ba1dd205", + "parsed": "25000000000" + } + ], + [ + "target", + { + "cl_type": { + "ByteArray": 32 + }, + "bytes": "6bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a", + "parsed": "6bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a" + } + ], + [ + "id", + { + "cl_type": { + "Option": "U64" + }, + "bytes": "010100000000000000", + "parsed": 1 + } + ] + ] + } + }, + "approvals": [ + { + "signer": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", + "signature": "01a42c794b4f9f1d344a41ec67f0a01a3f5c18b7a04c8948118d172993137d9db526c0e2512b89c45fc100d6cab9c562cccc295d6b0fd667a0c9d211cae88c570b" + } + ] + } + }, + "execution_info": { + "block_hash": "4ab00f303c62fe665fabf1112fc08b995c31b62b7af13390b417d8ba483597d4", + "block_height": 74, + "execution_result": { + "Version2": { + "initiator": { + "PublicKey": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a" + }, + "error_message": null, + "limit": "10000", + "consumed": "10000", + "cost": "10000", + "payment": [], + "transfers": [ + { + "Version2": { + "transaction_hash": { + "Deploy": "53363985887173c70955bee232e280429f53ab208e2397b19a5e761215734e56" + }, + "from": { + "AccountHash": "account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4" + }, + "to": "account-hash-6bccb64a7904b7217dd4ed7d9e4163785fe2133d45d390250b18f9442917bc0a", + "source": "uref-8b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce85142-007", + "target": "uref-98735896568d7b44005778ed909e83457e8923c76b2a23bcec09643cc384cfce-004", + "amount": "25000000000", + "gas": "0", + "id": 1 + } + } + ], + "size_estimate": 367, + "effects": [ + { + "key": "balance-hold-018b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce85142d0f57dbf90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "021027", + "parsed": "10000" + } + } + } + }, + { + "key": "account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4", + "kind": "Identity" + }, + { + "key": "balance-hold-008b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce8514256207dbf90010000", + "kind": "Identity" + }, + { + "key": "balance-hold-018b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce85142d0f57dbf90010000", + "kind": "Identity" + }, + { + "key": "balance-8b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce85142", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "0e005e6be5ec5ac138938d44c64d31", + "parsed": "999999999999999999999875000000000" + } + } + } + }, + { + "key": "balance-98735896568d7b44005778ed909e83457e8923c76b2a23bcec09643cc384cfce", + "kind": { + "AddUInt512": "25000000000" + } + }, + { + "key": "balance-hold-018b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce85142d0f57dbf90010000", + "kind": { + "Prune": "balance-hold-018b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce85142d0f57dbf90010000" + } + }, + { + "key": "balance-hold-008b3e0e98047445553ad20dae8fb539358e4421b49348e7ee8061e3ad5ce85142d0f57dbf90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "021027", + "parsed": "10000" + } + } + } + }, + { + "key": "entity-system-8499396a854bbe7e172a5ab675caf3c7b944f57e43cee5b7563971678f8650b2", + "kind": "Identity" + }, + { + "key": "entity-system-989625d28e779749e3cacc7367403dd459c8e7082937436f16d5dbff7aec8292", + "kind": "Identity" + }, + { + "key": "entity-system-da2faf76ab117ec370d10f994e9407ff014ab974bedc50e58425a9e35428dd0c", + "kind": "Identity" + }, + { + "key": "bid-addr-01dcd0c38c46c9d5c7083aa1a46b430e8e460f97f8f0bf8444776ac925187acfcc", + "kind": "Identity" + }, + { + "key": "bid-addr-04dcd0c38c46c9d5c7083aa1a46b430e8e460f97f8f0bf8444776ac925187acfcc0900000000000000", + "kind": { + "Write": { + "BidKind": { + "Credit": { + "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "era_id": 9, + "amount": "10000" + } + } + } + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json b/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json new file mode 100644 index 0000000..878f5e8 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json @@ -0,0 +1,1469 @@ +{ + "api_version": "2.0.0", + "transaction": { + "Version1": { + "hash": "45a4d4ce6ff79d7b49c34d2c7bdc55543d1734b1a03d66bea513e631ff21213b", + "header": { + "chain_name": "casper-net-1", + "timestamp": "2024-07-17T08:52:03.256Z", + "ttl": "30m", + "body_hash": "9b1d4002bb1af17ec9fc4193ac5ba7db6535b6dd813034b730829a94b13ebc46", + "pricing_mode": { + "Fixed": { + "gas_price_tolerance": 3 + } + }, + "initiator_addr": { + "PublicKey": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4" + } + }, + "body": { + "args": [ + [ + "name", + { + "cl_type": "String", + "bytes": "0b000000434c49434b312054657374", + "parsed": "CLICK1 Test" + } + ], + [ + "symbol", + { + "cl_type": "String", + "bytes": "06000000434c49434b31", + "parsed": "CLICK1" + } + ], + [ + "decimals", + { + "cl_type": "U8", + "bytes": "09", + "parsed": 9 + } + ], + [ + "total_supply", + { + "cl_type": "U256", + "bytes": "070080c6a47e8d03", + "parsed": "1000000000000000" + } + ], + [ + "events_mode", + { + "cl_type": "U8", + "bytes": "05", + "parsed": 5 + } + ], + [ + "enable_mint_burn", + { + "cl_type": "U8", + "bytes": "01", + "parsed": 1 + } + ] + ], + "target": { + "Session": { + "module_bytes": "01020304", + "runtime": "VmCasperV1" + } + }, + "entry_point": "Call", + "transaction_category": 2, + "scheduling": "Standard" + }, + "approvals": [ + { + "signer": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", + "signature": "016a910dafe6b87b8f12daa5698c33c0ab06eda11e79e06cacc446d3de65d85176fa26a0a9036ad80272aca8b1dfe674227e9cfaa98509e60439879d4fde487b04" + } + ] + } + }, + "execution_info": { + "block_hash": "b84c248b4c1bee3abf77e84b921fcc0d09290a1556285327922da4dc23de3177", + "block_height": 1809, + "execution_result": { + "Version2": { + "initiator": { + "PublicKey": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4" + }, + "error_message": null, + "limit": "1000000000000", + "consumed": "350280524938", + "cost": "1000000000000", + "payment": [], + "transfers": [], + "size_estimate": 303776, + "effects": [ + { + "key": "balance-hold-016f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee042215af3fe4bf90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "050010a5d4e8", + "parsed": "1000000000000" + } + } + } + }, + { + "key": "uref-5aa675065318455e193fc341bb4750174283add94ca195f71e4b4104e4c84b1a-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "String", + "bytes": "0b000000434c49434b312054657374", + "parsed": "CLICK1 Test" + } + } + } + }, + { + "key": "uref-977f30046644e5c7a7e8cb297e5f8971a1680543dc31d1066ab4870c575b8a0d-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "String", + "bytes": "06000000434c49434b31", + "parsed": "CLICK1" + } + } + } + }, + { + "key": "uref-63c283118dd23b4ceb6fb4eabe219941ca795a9588f5e09c1438cf7e4904d285-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U8", + "bytes": "09", + "parsed": 9 + } + } + } + }, + { + "key": "uref-5b2eb4a8703d23be50b30625811388876b10ea57c6f79e613a144b59bc8eea1d-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U256", + "bytes": "070080c6a47e8d03", + "parsed": "1000000000000000" + } + } + } + }, + { + "key": "uref-08f9c3e110c4d5cbc6e8ce71dd0cd535e4f41b34fe88c26cf3cd222004373db3-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U8", + "bytes": "05", + "parsed": 5 + } + } + } + }, + { + "key": "uref-ed2804ba22d80b1cc8abb02f72e1d87e93b3186d1225cf286b733e29c4df61e8-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U8", + "bytes": "01", + "parsed": 1 + } + } + } + }, + { + "key": "uref-5ef62926d2a73b5982cab9cf546b59e75cd72b1c57c5eaaf2a01c90705fb6b9b-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + } + }, + { + "key": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", + "kind": { + "Write": { + "Package": { + "versions": [], + "disabled_versions": [], + "groups": [], + "lock_status": "Unlocked" + } + } + } + }, + { + "key": "named-key-entity-account-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f-fc71cc1c9046f7ac32c20c9f9897c6c3a0b6bb288e9e54eb29202867ca9b0d94", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "10ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", + "parsed": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4" + }, + "name": { + "cl_type": "String", + "bytes": "2200000063657031385f636f6e74726163745f7061636b6167655f434c49434b312054657374", + "parsed": "cep18_contract_package_CLICK1 Test" + } + } + } + } + }, + { + "key": "named-key-entity-account-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f-715bf96dd4d151db8e1135387b051ea188d8fc85ca004740d4f181c6ad038731", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "025ef62926d2a73b5982cab9cf546b59e75cd72b1c57c5eaaf2a01c90705fb6b9b07", + "parsed": "uref-5ef62926d2a73b5982cab9cf546b59e75cd72b1c57c5eaaf2a01c90705fb6b9b-007" + }, + "name": { + "cl_type": "String", + "bytes": "2900000063657031385f636f6e74726163745f7061636b6167655f6163636573735f434c49434b312054657374", + "parsed": "cep18_contract_package_access_CLICK1 Test" + } + } + } + } + }, + { + "key": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", + "kind": "Identity" + }, + { + "key": "entity-system-989625d28e779749e3cacc7367403dd459c8e7082937436f16d5dbff7aec8292", + "kind": "Identity" + }, + { + "key": "package-b94115849598c8b83bb94dd7ea211630a9e23827ceb089a8674e1518a8257a1a", + "kind": "Identity" + }, + { + "key": "entry-point-v1-entity-system-989625d28e779749e3cacc7367403dd459c8e7082937436f16d5dbff7aec8292-21bddc7e4379ba445c7118cb51962954e0d1e5aa5cacc0c4ff6095b57eb9fb33", + "kind": "Identity" + }, + { + "key": "uref-2b130d8c617d24bd5366bdd2843db98a5e93e594ed827ace443b4ea6e4772f01-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + } + }, + { + "key": "balance-2b130d8c617d24bd5366bdd2843db98a5e93e594ed827ace443b4ea6e4772f01", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "00", + "parsed": "0" + } + } + } + }, + { + "key": "byte-code-v1-wasm-800e35cfd87b0f36479afdaf68725cd7ef0f503e8d541ed33afad4a18c7fa6eb", + "kind": { + "Write": { + "ByteCode": { + "kind": "V1CasperWasm", + "bytes": "01020304" + } + } + } + }, + { + "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-be138e764d5f26cd174471e18c82a7bef961da4c7e7ade7df068038aebdda9bf", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "0263c283118dd23b4ceb6fb4eabe219941ca795a9588f5e09c1438cf7e4904d28507", + "parsed": "uref-63c283118dd23b4ceb6fb4eabe219941ca795a9588f5e09c1438cf7e4904d285-007" + }, + "name": { + "cl_type": "String", + "bytes": "08000000646563696d616c73", + "parsed": "decimals" + } + } + } + } + }, + { + "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-f26520fac960fb1abac44a358923ab6a064baffb1707c885886d157f66c55209", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "02ed2804ba22d80b1cc8abb02f72e1d87e93b3186d1225cf286b733e29c4df61e807", + "parsed": "uref-ed2804ba22d80b1cc8abb02f72e1d87e93b3186d1225cf286b733e29c4df61e8-007" + }, + "name": { + "cl_type": "String", + "bytes": "10000000656e61626c655f6d696e745f6275726e", + "parsed": "enable_mint_burn" + } + } + } + } + }, + { + "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-7114a751e72d65a0290c975396374e0120ac8f3ddbb6e4e21e3c6810135b40d0", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "0208f9c3e110c4d5cbc6e8ce71dd0cd535e4f41b34fe88c26cf3cd222004373db307", + "parsed": "uref-08f9c3e110c4d5cbc6e8ce71dd0cd535e4f41b34fe88c26cf3cd222004373db3-007" + }, + "name": { + "cl_type": "String", + "bytes": "0b0000006576656e74735f6d6f6465", + "parsed": "events_mode" + } + } + } + } + }, + { + "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-69cade231fc487185af830cfe041ce668a4763ab02ee5989b8baac6bee7e1a22", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "025aa675065318455e193fc341bb4750174283add94ca195f71e4b4104e4c84b1a07", + "parsed": "uref-5aa675065318455e193fc341bb4750174283add94ca195f71e4b4104e4c84b1a-007" + }, + "name": { + "cl_type": "String", + "bytes": "040000006e616d65", + "parsed": "name" + } + } + } + } + }, + { + "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-eef0c71bbea5a76f1da01cb395e12bc0388bec279852100e17e3843b3e559999", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "02977f30046644e5c7a7e8cb297e5f8971a1680543dc31d1066ab4870c575b8a0d07", + "parsed": "uref-977f30046644e5c7a7e8cb297e5f8971a1680543dc31d1066ab4870c575b8a0d-007" + }, + "name": { + "cl_type": "String", + "bytes": "0600000073796d626f6c", + "parsed": "symbol" + } + } + } + } + }, + { + "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-f99c57f016ee238df5bcdf8bec27869b1ba087a415050a9c6668644eeda11af0", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "025b2eb4a8703d23be50b30625811388876b10ea57c6f79e613a144b59bc8eea1d07", + "parsed": "uref-5b2eb4a8703d23be50b30625811388876b10ea57c6f79e613a144b59bc8eea1d-007" + }, + "name": { + "cl_type": "String", + "bytes": "0c000000746f74616c5f737570706c79", + "parsed": "total_supply" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-1e3c8f46f39b1ee1cbadb774ffaf842226b7cbf1fef3bbc04abfa80b86daca11", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "allowance", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "spender", + "cl_type": "Key" + } + ], + "ret": "U256", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-44528c1898e30df62037a76e0c45123f4f4437336ca63236b10ebfc16a5edb78", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "approve", + "args": [ + { + "name": "spender", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-fcc296caa05679d0d11121e7629b29f222a857018f50985046b73a56e9a10701", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "balance_of", + "args": [ + { + "name": "address", + "cl_type": "Key" + } + ], + "ret": "U256", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-768c370eb010604bd19029a409dca8b5fbf9af9bc14a36c2b294a2a7a922161e", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "burn", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-bab8615f758ed79acb7dd7577b1a6c12d625d1a19592a2b1ded0dc352407e4d5", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "change_events_mode", + "args": [ + { + "name": "events_mode", + "cl_type": "U8" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-82a811993cf9ccb5e46c9608c69d86e3c9b7b499520fd48cdca1424f2a08efdc", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "change_security", + "args": [], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-45ffbf1854843af5eeec6b167e14a9e97bdb526e66205b07559d4fb3928fb11e", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "condor", + "args": [], + "ret": "String", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-be138e764d5f26cd174471e18c82a7bef961da4c7e7ade7df068038aebdda9bf", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "decimals", + "args": [], + "ret": "U8", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-ac07c23dc90a33282d553af890e30e62335c5ae986629d643778e2d4516f26ad", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "decrease_allowance", + "args": [ + { + "name": "spender", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-a7e05838c728d16c4ba3e1980b6729c857ef4c21d1b0c34e6eefbb486cdc2b89", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "increase_allowance", + "args": [ + { + "name": "spender", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-4ca60287ae6129662475a8ce0d41c450d072b2430a8759f6178adeeff38523da", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "init", + "args": [], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-fc79236fd0e4521c8feddcc2094c6a0ea04fcaafb17fef63ef060744a6bab401", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "migrate_sec_keys", + "args": [ + { + "name": "events", + "cl_type": "Bool" + }, + { + "name": "revert", + "cl_type": "Bool" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-18bff854e9d908cf20fb1db53a47ab69968917b53b8c71371e7dd0f88b363e60", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "migrate_user_allowance_keys", + "args": [ + { + "name": "events", + "cl_type": "Bool" + }, + { + "name": "revert", + "cl_type": "Bool" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-1e7babf918642bd9636d3c121691bd85534b41084a5c22fe1e2bf196224dade6", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "migrate_user_balance_keys", + "args": [ + { + "name": "events", + "cl_type": "Bool" + }, + { + "name": "revert", + "cl_type": "Bool" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-233964bb1dc667b37a8abbb938d7647b2c4ab41f0c26dbbcd26c62e7870f72ba", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "mint", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-69cade231fc487185af830cfe041ce668a4763ab02ee5989b8baac6bee7e1a22", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "name", + "args": [], + "ret": "String", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-eef0c71bbea5a76f1da01cb395e12bc0388bec279852100e17e3843b3e559999", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "symbol", + "args": [], + "ret": "String", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-f99c57f016ee238df5bcdf8bec27869b1ba087a415050a9c6668644eeda11af0", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "total_supply", + "args": [], + "ret": "U256", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-3820ce25e54df0470fb738e3e0f63ee50b2719cf2680da2bdb579e21aebc8f63", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "transfer", + "args": [ + { + "name": "recipient", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-44208043191e40d3417df6878e1a23894172cf37cf2e4444384ada99e25430e7", + "kind": { + "Write": { + "EntryPoint": { + "V1CasperVm": { + "name": "transfer_from", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "recipient", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called", + "entry_point_payment": "Caller" + } + } + } + } + }, + { + "key": "entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58", + "kind": { + "Write": { + "AddressableEntity": { + "protocol_version": "2.0.0", + "entity_kind": { + "SmartContract": "VmCasperV1" + }, + "package_hash": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", + "byte_code_hash": "byte-code-800e35cfd87b0f36479afdaf68725cd7ef0f503e8d541ed33afad4a18c7fa6eb", + "main_purse": "uref-2b130d8c617d24bd5366bdd2843db98a5e93e594ed827ace443b4ea6e4772f01-007", + "associated_keys": [ + { + "account_hash": "account-hash-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f", + "weight": 1 + } + ], + "action_thresholds": { + "deployment": 1, + "upgrade_management": 1, + "key_management": 1 + }, + "message_topics": [ + { + "topic_name": "errors", + "topic_name_hash": "b38b3a8f7a7cb169b9869f1b660e328df63941f4f078d284a0058140375ec7fc" + }, + { + "topic_name": "events", + "topic_name_hash": "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1" + } + ] + } + } + } + }, + { + "key": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", + "kind": { + "Write": { + "Package": { + "versions": [ + { + "entity_version_key": { + "protocol_version_major": 2, + "entity_version": 1 + }, + "addressable_entity_hash": "addressable-entity-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58" + } + ], + "disabled_versions": [], + "groups": [], + "lock_status": "Unlocked" + } + } + } + }, + { + "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-b38b3a8f7a7cb169b9869f1b660e328df63941f4f078d284a0058140375ec7fc", + "kind": { + "Write": { + "MessageTopic": { + "message_count": 0, + "blocktime": 1721206325167 + } + } + } + }, + { + "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "kind": { + "Write": { + "MessageTopic": { + "message_count": 0, + "blocktime": 1721206325167 + } + } + } + }, + { + "key": "named-key-entity-account-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f-1b97fc9e53ff59b389432fd44a90c5d773bb65a2933b8196a5c441724819f8d6", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "1102d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58", + "parsed": "entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58" + }, + "name": { + "cl_type": "String", + "bytes": "1f00000063657031385f636f6e74726163745f686173685f434c49434b312054657374", + "parsed": "cep18_contract_hash_CLICK1 Test" + } + } + } + } + }, + { + "key": "uref-a6b391748c16be8f8c000b5658884cea53332255596e0537667e71f2d358af90-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U32", + "bytes": "01000000", + "parsed": 1 + } + } + } + }, + { + "key": "named-key-entity-account-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f-50e82d25e72fda1f67a69b3ac3a3a56565971c80577ba72df18d6fbd73f2bb1d", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "02a6b391748c16be8f8c000b5658884cea53332255596e0537667e71f2d358af9007", + "parsed": "uref-a6b391748c16be8f8c000b5658884cea53332255596e0537667e71f2d358af90-007" + }, + "name": { + "cl_type": "String", + "bytes": "2200000063657031385f636f6e74726163745f76657273696f6e5f434c49434b312054657374", + "parsed": "cep18_contract_version_CLICK1 Test" + } + } + } + } + }, + { + "key": "uref-419179eebe4ea6427e77a58aae6fac945c4d4135d042ff800d0be71ac17d7a8d-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "String", + "bytes": "06000000636f6e646f72", + "parsed": "condor" + } + } + } + }, + { + "key": "named-key-entity-account-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f-45ffbf1854843af5eeec6b167e14a9e97bdb526e66205b07559d4fb3928fb11e", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "02419179eebe4ea6427e77a58aae6fac945c4d4135d042ff800d0be71ac17d7a8d07", + "parsed": "uref-419179eebe4ea6427e77a58aae6fac945c4d4135d042ff800d0be71ac17d7a8d-007" + }, + "name": { + "cl_type": "String", + "bytes": "06000000636f6e646f72", + "parsed": "condor" + } + } + } + } + }, + { + "key": "entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58", + "kind": "Identity" + }, + { + "key": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", + "kind": "Identity" + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-4ca60287ae6129662475a8ce0d41c450d072b2430a8759f6178adeeff38523da", + "kind": "Identity" + }, + { + "key": "byte-code-v1-wasm-800e35cfd87b0f36479afdaf68725cd7ef0f503e8d541ed33afad4a18c7fa6eb", + "kind": "Identity" + }, + { + "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-c45567f4859db5b100d4c45a9638973ccdf0a7068a2a50f7a1c8709d5e84fbd2", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "10ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", + "parsed": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4" + }, + "name": { + "cl_type": "String", + "bytes": "0c0000007061636b6167655f68617368", + "parsed": "package_hash" + } + } + } + } + }, + { + "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-d447c1e29c0d374421e2c3f290c580a73aa748eba54e1b6bb3da0acdd4cdcee5", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "1102d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58", + "parsed": "entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58" + }, + "name": { + "cl_type": "String", + "bytes": "0d000000636f6e74726163745f68617368", + "parsed": "contract_hash" + } + } + } + } + }, + { + "key": "uref-e164c15c5fb035160a0d105a467fa4b6e7562746ffd2bdad6e5e3220878df19a-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + } + }, + { + "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-bce0df2b23ec57062235795dd9b7a1a6dc07885918c6cee5c5859a9df3d498e0", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "02e164c15c5fb035160a0d105a467fa4b6e7562746ffd2bdad6e5e3220878df19a07", + "parsed": "uref-e164c15c5fb035160a0d105a467fa4b6e7562746ffd2bdad6e5e3220878df19a-007" + }, + "name": { + "cl_type": "String", + "bytes": "0a000000616c6c6f77616e636573", + "parsed": "allowances" + } + } + } + } + }, + { + "key": "uref-f032be9ae9bd295ec0403aa886d7247d27c296751176bd88d3b02c996626fd05-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + } + }, + { + "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-058ed80a0c85bcf8470ac5b8ca4c14f1086c3cd5ae00f1f0592fee3addf4073c", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "02f032be9ae9bd295ec0403aa886d7247d27c296751176bd88d3b02c996626fd0507", + "parsed": "uref-f032be9ae9bd295ec0403aa886d7247d27c296751176bd88d3b02c996626fd05-007" + }, + "name": { + "cl_type": "String", + "bytes": "0800000062616c616e636573", + "parsed": "balances" + } + } + } + } + }, + { + "key": "dictionary-ede600b099900d357796a3e23eecb1e5277bdfb93297c36f0c039d2c3a5c6557", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "08000000070080c6a47e8d030720000000f032be9ae9bd295ec0403aa886d7247d27c296751176bd88d3b02c996626fd053000000045514869587777376d477171476d7946376a5672365a7a5449506f66664f72356b6f6f2f765146647352386b44773d3d", + "parsed": null + } + } + } + }, + { + "key": "uref-6e6c902ff1c50971ab62f788d0fe0171ebffceb91be9861c02ce55993ae6bcdc-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + } + }, + { + "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5f4fd818ad44d4ae056e151759a8585de97a1c7c4d53ceecac6631f9fbb39ab6", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "026e6c902ff1c50971ab62f788d0fe0171ebffceb91be9861c02ce55993ae6bcdc07", + "parsed": "uref-6e6c902ff1c50971ab62f788d0fe0171ebffceb91be9861c02ce55993ae6bcdc-007" + }, + "name": { + "cl_type": "String", + "bytes": "0f00000073656375726974795f626164676573", + "parsed": "security_badges" + } + } + } + } + }, + { + "key": "dictionary-30b94b42bb8ec2d32652ac73cf74031df44c1327c946924c5c9ffe82a94b3dd0", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "010000000003200000006e6c902ff1c50971ab62f788d0fe0171ebffceb91be9861c02ce55993ae6bcdc3000000045514869587777376d477171476d7946376a5672365a7a5449506f66664f72356b6f6f2f765146647352386b44773d3d", + "parsed": null + } + } + } + }, + { + "key": "uref-08f9c3e110c4d5cbc6e8ce71dd0cd535e4f41b34fe88c26cf3cd222004373db3-000", + "kind": "Identity" + }, + { + "key": "uref-9d1bc561e4c28250e3595b12b78989409f69ad336a8e398b4e9f6b91b3c28317-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + } + }, + { + "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-0c12f81c2baaa55048b1b7f396d105681463f5ce8f43e9de903369fb55f29947", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "029d1bc561e4c28250e3595b12b78989409f69ad336a8e398b4e9f6b91b3c2831707", + "parsed": "uref-9d1bc561e4c28250e3595b12b78989409f69ad336a8e398b4e9f6b91b3c28317-007" + }, + "name": { + "cl_type": "String", + "bytes": "080000005f5f6576656e7473", + "parsed": "__events" + } + } + } + } + }, + { + "key": "uref-ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae4-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U32", + "bytes": "00000000", + "parsed": 0 + } + } + } + }, + { + "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-dd93feca0896fb86350700d69861072bad6eae9c93e5f2ffeb43f2daa4f6fcc1", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "02ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae407", + "parsed": "uref-ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae4-007" + }, + "name": { + "cl_type": "String", + "bytes": "0f0000005f5f6576656e74735f6c656e677468", + "parsed": "__events_length" + } + } + } + } + }, + { + "key": "uref-8f51f4b1f048ba99b5b14e1021cfdb743af05c5c4452dd0350c5649a48cd3d09-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": { + "Map": { + "key": "String", + "value": { + "List": { + "Tuple2": [ + "String", + "Any" + ] + } + } + } + }, + "bytes": "09000000040000004275726e02000000050000006f776e65720b06000000616d6f756e7407100000004368616e67654576656e74734d6f6465010000000b0000006576656e74735f6d6f6465030e0000004368616e67655365637572697479020000000500000061646d696e0b0e0000007365635f6368616e67655f6d6170110b03110000004465637265617365416c6c6f77616e636504000000050000006f776e65720b070000007370656e6465720b09000000616c6c6f77616e63650707000000646563725f62790711000000496e637265617365416c6c6f77616e636504000000050000006f776e65720b070000007370656e6465720b09000000616c6c6f77616e63650706000000696e635f627907040000004d696e740200000009000000726563697069656e740b06000000616d6f756e74070c000000536574416c6c6f77616e636503000000050000006f776e65720b070000007370656e6465720b09000000616c6c6f77616e636507080000005472616e73666572030000000600000073656e6465720b09000000726563697069656e740b06000000616d6f756e74070c0000005472616e7366657246726f6d04000000070000007370656e6465720b050000006f776e65720b09000000726563697069656e740b06000000616d6f756e7407", + "parsed": null + } + } + } + }, + { + "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-1668218bcb4abcca6eb423a234252322df6319372653092475d173c00714a089", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "028f51f4b1f048ba99b5b14e1021cfdb743af05c5c4452dd0350c5649a48cd3d0907", + "parsed": "uref-8f51f4b1f048ba99b5b14e1021cfdb743af05c5c4452dd0350c5649a48cd3d09-007" + }, + "name": { + "cl_type": "String", + "bytes": "0f0000005f5f6576656e74735f736368656d61", + "parsed": "__events_schema" + } + } + } + } + }, + { + "key": "uref-8474015e6fe46390ce16ebe4f7f6dd9d88b9a1def98ae4c32f8808b53f66d582-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "String", + "bytes": "03000000312e31", + "parsed": "1.1" + } + } + } + }, + { + "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-6cb1f5fa1d62a0d84f41d492ee1526320c547fd54ef072398e9aec731475e9d0", + "kind": { + "Write": { + "NamedKey": { + "named_key": { + "cl_type": "Key", + "bytes": "028474015e6fe46390ce16ebe4f7f6dd9d88b9a1def98ae4c32f8808b53f66d58207", + "parsed": "uref-8474015e6fe46390ce16ebe4f7f6dd9d88b9a1def98ae4c32f8808b53f66d582-007" + }, + "name": { + "cl_type": "String", + "bytes": "140000005f5f6576656e74735f6365735f76657273696f6e", + "parsed": "__events_ces_version" + } + } + } + } + }, + { + "key": "uref-08f9c3e110c4d5cbc6e8ce71dd0cd535e4f41b34fe88c26cf3cd222004373db3-000", + "kind": "Identity" + }, + { + "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "kind": "Identity" + }, + { + "key": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-0", + "kind": { + "Write": { + "Message": "message-checksum-de44efda117cc02be18edf564107308f982498f7a781e7496f7f20d05957bb3c" + } + } + }, + { + "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "kind": { + "Write": { + "MessageTopic": { + "message_count": 1, + "blocktime": 1721206325167 + } + } + } + }, + { + "key": "block-message-count-00000000000000000000000000000000000000000000000000000000000000", + "kind": { + "Write": { + "CLValue": { + "cl_type": { + "Tuple2": [ + "U64", + "U64" + ] + }, + "bytes": "af3fe4bf900100000100000000000000", + "parsed": [ + 1721206325167, + 1 + ] + } + } + } + }, + { + "key": "uref-ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae4-000", + "kind": "Identity" + }, + { + "key": "dictionary-17125c888001cf5f36a03240f55838b5d9e09172292589fff4a7b25ae6e6c5b2", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "3c000000380000000a0000006576656e745f4d696e741101e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f070080c6a47e8d030e03200000009d1bc561e4c28250e3595b12b78989409f69ad336a8e398b4e9f6b91b3c283170100000030", + "parsed": null + } + } + } + }, + { + "key": "uref-ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae4-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U32", + "bytes": "01000000", + "parsed": 1 + } + } + } + }, + { + "key": "balance-hold-016f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee042215af3fe4bf90010000", + "kind": { + "Prune": "balance-hold-016f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee042215af3fe4bf90010000" + } + }, + { + "key": "balance-hold-006f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee042215af3fe4bf90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "050010a5d4e8", + "parsed": "1000000000000" + } + } + } + }, + { + "key": "entity-system-8499396a854bbe7e172a5ab675caf3c7b944f57e43cee5b7563971678f8650b2", + "kind": "Identity" + }, + { + "key": "entity-system-989625d28e779749e3cacc7367403dd459c8e7082937436f16d5dbff7aec8292", + "kind": "Identity" + }, + { + "key": "entity-system-da2faf76ab117ec370d10f994e9407ff014ab974bedc50e58425a9e35428dd0c", + "kind": "Identity" + }, + { + "key": "bid-addr-011c50d14ca563d5afe0399a3da010bac01383f502bc7cce774cc03d858a94cf06", + "kind": "Identity" + }, + { + "key": "bid-addr-041c50d14ca563d5afe0399a3da010bac01383f502bc7cce774cc03d858a94cf06a700000000000000", + "kind": { + "Write": { + "BidKind": { + "Credit": { + "validator_public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "era_id": 167, + "amount": "1000000000000" + } + } + } + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/get-transaction-stored-v200.json b/Casper.Network.SDK.Test/TestData/get-transaction-stored-v200.json new file mode 100644 index 0000000..7fed4b1 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-transaction-stored-v200.json @@ -0,0 +1,279 @@ +{ + "api_version": "2.0.0", + "transaction": { + "Version1": { + "hash": "3fe455e770f5464234b1321bcb6bd02c553649f9a8a468e5a37570f24631968d", + "header": { + "chain_name": "casper-net-1", + "timestamp": "2024-07-17T09:03:11.866Z", + "ttl": "30m", + "body_hash": "07ed52f990a206153d2a29f6a42eb754009c4b120981dd3fd0842d4057ee7484", + "pricing_mode": { + "Fixed": { + "gas_price_tolerance": 1 + } + }, + "initiator_addr": { + "AccountHash": "account-hash-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f" + } + }, + "body": { + "args": [ + [ + "recipient", + { + "cl_type": "Key", + "bytes": "00e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f", + "parsed": "account-hash-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f" + } + ], + [ + "amount", + { + "cl_type": "U256", + "bytes": "02d930", + "parsed": "12505" + } + ] + ], + "target": { + "Stored": { + "id": { + "ByPackageName": { + "name": "cep18_contract_package_CLICK1 Test", + "version": null + } + }, + "runtime": "VmCasperV1" + } + }, + "entry_point": { + "Custom": "transfer" + }, + "transaction_category": 4, + "scheduling": "Standard" + }, + "approvals": [ + { + "signer": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", + "signature": "010641446be631a751415fd88827bd0c804a390824c48b7f1272f37b827853dcf3ae5710e662570d28dab8a283994f4f096cbd1d4ebb2f7fd24fb05b2b9b61640f" + } + ] + } + }, + "execution_info": { + "block_hash": "b6446a0508cbf2bffdf855213ea6eb43267c4dfacd667b50cf8d8f0098112a3b", + "block_height": 1984, + "execution_result": { + "Version2": { + "initiator": { + "AccountHash": "account-hash-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f" + }, + "error_message": null, + "limit": "50000000000", + "consumed": "763310980", + "cost": "50000000000", + "payment": [], + "transfers": [], + "size_estimate": 363, + "effects": [ + { + "key": "balance-hold-016f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee0422150b78eebf90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "0500743ba40b", + "parsed": "50000000000" + } + } + } + }, + { + "key": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", + "kind": "Identity" + }, + { + "key": "entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58", + "kind": "Identity" + }, + { + "key": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", + "kind": "Identity" + }, + { + "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-3820ce25e54df0470fb738e3e0f63ee50b2719cf2680da2bdb579e21aebc8f63", + "kind": "Identity" + }, + { + "key": "byte-code-v1-wasm-800e35cfd87b0f36479afdaf68725cd7ef0f503e8d541ed33afad4a18c7fa6eb", + "kind": "Identity" + }, + { + "key": "dictionary-ede600b099900d357796a3e23eecb1e5277bdfb93297c36f0c039d2c3a5c6557", + "kind": "Identity" + }, + { + "key": "dictionary-b25d5021fc15e97434b4e8128804f5c8e7ce21e65bbe80d0ec344c23f93c7582", + "kind": "Identity" + }, + { + "key": "dictionary-ede600b099900d357796a3e23eecb1e5277bdfb93297c36f0c039d2c3a5c6557", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "0800000007c38bc5a47e8d030720000000f032be9ae9bd295ec0403aa886d7247d27c296751176bd88d3b02c996626fd053000000045514869587777376d477171476d7946376a5672365a7a5449506f66664f72356b6f6f2f765146647352386b44773d3d", + "parsed": null + } + } + } + }, + { + "key": "dictionary-b25d5021fc15e97434b4e8128804f5c8e7ce21e65bbe80d0ec344c23f93c7582", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "03000000023df40720000000f032be9ae9bd295ec0403aa886d7247d27c296751176bd88d3b02c996626fd052c000000414f4a664444755961716f61624958754e5776706e4e4d672b68393836766d53696a2b394156327848795150", + "parsed": null + } + } + } + }, + { + "key": "uref-08f9c3e110c4d5cbc6e8ce71dd0cd535e4f41b34fe88c26cf3cd222004373db3-000", + "kind": "Identity" + }, + { + "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "kind": "Identity" + }, + { + "key": "block-message-count-00000000000000000000000000000000000000000000000000000000000000", + "kind": "Identity" + }, + { + "key": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-1", + "kind": { + "Write": { + "Message": "message-checksum-f6a3531f88cf8e763916a16e95a64e2ef93b0567064830ad5912523fad71fbe2" + } + } + }, + { + "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "kind": { + "Write": { + "MessageTopic": { + "message_count": 2, + "blocktime": 1721206994955 + } + } + } + }, + { + "key": "block-message-count-00000000000000000000000000000000000000000000000000000000000000", + "kind": { + "Write": { + "CLValue": { + "cl_type": { + "Tuple2": [ + "U64", + "U64" + ] + }, + "bytes": "0b78eebf900100000200000000000000", + "parsed": [ + 1721206994955, + 2 + ] + } + } + } + }, + { + "key": "uref-ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae4-000", + "kind": "Identity" + }, + { + "key": "dictionary-55775426d9d4425e15abc8f1e7241ef945379bf5037bb346e3ca15a4fe1ccb50", + "kind": { + "Write": { + "CLValue": { + "cl_type": "Any", + "bytes": "5c000000580000000e0000006576656e745f5472616e736665721101e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f00e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f02d9300e03200000009d1bc561e4c28250e3595b12b78989409f69ad336a8e398b4e9f6b91b3c283170100000035", + "parsed": null + } + } + } + }, + { + "key": "uref-ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae4-000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U32", + "bytes": "06000000", + "parsed": 6 + } + } + } + }, + { + "key": "balance-hold-016f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee0422150b78eebf90010000", + "kind": { + "Prune": "balance-hold-016f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee0422150b78eebf90010000" + } + }, + { + "key": "balance-hold-006f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee0422150b78eebf90010000", + "kind": { + "Write": { + "CLValue": { + "cl_type": "U512", + "bytes": "0500743ba40b", + "parsed": "50000000000" + } + } + } + }, + { + "key": "entity-system-8499396a854bbe7e172a5ab675caf3c7b944f57e43cee5b7563971678f8650b2", + "kind": "Identity" + }, + { + "key": "entity-system-989625d28e779749e3cacc7367403dd459c8e7082937436f16d5dbff7aec8292", + "kind": "Identity" + }, + { + "key": "entity-system-da2faf76ab117ec370d10f994e9407ff014ab974bedc50e58425a9e35428dd0c", + "kind": "Identity" + }, + { + "key": "bid-addr-0165a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b1566213", + "kind": "Identity" + }, + { + "key": "bid-addr-0465a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b1566213b700000000000000", + "kind": "Identity" + }, + { + "key": "bid-addr-0465a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b1566213b700000000000000", + "kind": { + "Write": { + "BidKind": { + "Credit": { + "validator_public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "era_id": 183, + "amount": "50000000000" + } + } + } + } + } + ] + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs b/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs index 4b8b5cc..3678025 100644 --- a/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs +++ b/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs @@ -7,84 +7,98 @@ namespace Casper.Network.SDK.ByteSerializers { public class TransactionV1ByteSerializer : BaseByteSerializer, IByteSerializer { - public byte[] ToBytes(TransactionTarget source) + public byte[] ToBytes(ITransactionV1Target source) { var ms = new MemoryStream(); - WriteByte(ms, (byte)source.Type); - - if (source.Type == TransactionTargetType.Stored) + switch (source) { - switch (source.Id) + case NativeTransactionV1Target: + WriteByte(ms, (byte)TransactionTargetType.Native); + break; + case StoredTransactionV1Target storedTarget: + WriteByte(ms, (byte)TransactionTargetType.Stored); + switch (storedTarget.Id) + { + case ByHashInvocationTarget byHash: + WriteByte(ms, (byte)InvocationTargetTag.ByHash); + WriteBytes(ms, Hex.Decode(byHash.Hash)); + break; + case ByNameInvocationTarget byName: + WriteByte(ms, (byte)InvocationTargetTag.ByName); + WriteString(ms, byName.Name); + break; + case ByPackageHashInvocationTarget byPackageHash: + WriteByte(ms, (byte)InvocationTargetTag.ByPackageHash); + WriteBytes(ms, Hex.Decode(byPackageHash.Hash)); + WriteMaybeUInteger(ms, byPackageHash.Version); + break; + case ByPackageNameInvocationTarget byPackageName: + WriteByte(ms, (byte)InvocationTargetTag.ByPackageName); + WriteString(ms, byPackageName.Name); + WriteMaybeUInteger(ms, byPackageName.Version); + break; + } + WriteByte(ms, (byte)storedTarget.Runtime); + break; + case SessionTransactionV1Target sessionTarget: { - case ByHashInvocationTarget byHash: - WriteByte(ms, (byte)InvocationTargetTag.ByHash); - WriteBytes(ms, Hex.Decode(byHash.Hash)); - break; - case ByNameInvocationTarget byName: - WriteByte(ms, (byte)InvocationTargetTag.ByName); - WriteString(ms, byName.Name); - break; - case ByPackageHashInvocationTarget byPackageHash: - WriteByte(ms, (byte)InvocationTargetTag.ByPackageHash); - WriteBytes(ms, Hex.Decode(byPackageHash.Addr)); - WriteMaybeUInteger(ms, byPackageHash.Version); - break; - case ByPackageNameInvocationTarget byPackageName: - WriteByte(ms, (byte)InvocationTargetTag.ByPackageName); - WriteString(ms, byPackageName.Name); - WriteMaybeUInteger(ms, byPackageName.Version); - break; + WriteByte(ms, (byte)TransactionTargetType.Session); + + if (sessionTarget.ModuleBytes == null || sessionTarget.ModuleBytes.Length == 0) + WriteInteger(ms, 0); + else + { + WriteInteger(ms, sessionTarget.ModuleBytes.Length); + WriteBytes(ms, sessionTarget.ModuleBytes); + } + + WriteByte(ms, (byte)sessionTarget.Runtime); + break; } - WriteByte(ms, (byte)source.Runtime); - } - else if (source.Type == TransactionTargetType.Session) - { - if (source.ModuleBytes == null || source.ModuleBytes.Length == 0) - WriteInteger(ms, 0); - else - { - WriteInteger(ms, source.ModuleBytes.Length); - WriteBytes(ms, source.ModuleBytes); - } - - WriteByte(ms, (byte)source.Runtime); } return ms.ToArray(); } - public byte[] ToBytes(TransactionEntryPoint source) + public byte[] ToBytes(ITransactionV1EntryPoint source) { var ms = new MemoryStream(); - if (source.Custom != null) - { - WriteByte(ms, 0x00); - WriteString(ms, source.Custom); - } - else if (source.Native.HasValue) + switch (source) { - WriteByte(ms, (byte)source.Native.Value); - } - else - { - throw new Exception("Cannot serialize empty TransactionEntryPoint to bytes"); + case CustomTransactionV1EntryPoint customEntryPoint: + WriteByte(ms, 0x00); + WriteString(ms, customEntryPoint.Name); + break; + case NativeTransactionV1EntryPoint nativeEntryPoint: + WriteByte(ms, (byte)nativeEntryPoint.Type); + break; + default: + throw new Exception("Cannot serialize empty TransactionEntryPoint to bytes"); } return ms.ToArray(); } - public byte[] ToBytes(TransactionScheduling source) + public byte[] ToBytes(ITransactionV1Scheduling source) { var ms = new MemoryStream(); - WriteByte(ms, (byte)source.Type); - - if (source.Type == TransactionSchedulingType.FutureEra) - WriteULong(ms, source.EraId); - if (source.Type == TransactionSchedulingType.FutureTimestamp) - WriteULong(ms, source.Timestamp); + switch (source) + { + case StandardTransactionV1Scheduling: + WriteByte(ms, (byte)TransactionV1SchedulingType.Standard); + break; + case FutureEraTransactionV1Scheduling eraScheduling: + WriteByte(ms, (byte)TransactionV1SchedulingType.FutureEra); + WriteULong(ms, eraScheduling.EraId); + break; + case FutureTimestampTransactionV1Scheduling timestampScheduling: + WriteByte(ms, (byte)TransactionV1SchedulingType.FutureTimestamp); + WriteULong(ms, timestampScheduling.Timestamp); + break; + } return ms.ToArray(); } @@ -101,7 +115,7 @@ public byte[] ToBytes(TransactionV1Body source) WriteBytes(ms, ToBytes(source.Target)); WriteBytes(ms, ToBytes(source.EntryPoint)); - WriteByte(ms, source.TransactionCategory); + WriteByte(ms, (byte)source.Category); WriteBytes(ms, ToBytes(source.Scheduling)); return ms.ToArray(); @@ -111,27 +125,23 @@ public byte[] ToBytes(PricingMode source) { var ms = new MemoryStream(); - if (source.Type == PricingModeType.Classic && - source.PaymentAmount.HasValue && - source.GasPriceTolerance.HasValue && - source.StandardPayment.HasValue) - { - ms.WriteByte((byte)PricingModeType.Classic); - WriteULong(ms, (ulong)source.PaymentAmount.Value); - WriteByte(ms, (byte)source.GasPriceTolerance.Value); - WriteByte(ms, (byte)(source.StandardPayment.Value ? 0x01 : 0x00)); - } - else if (source.Type == PricingModeType.Fixed && - source.GasPriceTolerance.HasValue) - { - ms.WriteByte((byte)PricingModeType.Fixed); - WriteByte(ms, (byte)source.GasPriceTolerance.Value); - } - else if (source.Type == PricingModeType.Reserved && - source.Receipt != null) + switch (source.Type) { - ms.WriteByte((byte)PricingModeType.Reserved); - WriteBytes(ms, Hex.Decode(source.Receipt)); + case PricingModeType.Classic: + ms.WriteByte((byte)PricingModeType.Classic); + WriteULong(ms, (ulong)source.PaymentAmount); + WriteByte(ms, (byte)source.GasPriceTolerance); + WriteByte(ms, (byte)(source.StandardPayment ? 0x01 : 0x00)); + break; + case PricingModeType.Fixed: + ms.WriteByte((byte)PricingModeType.Fixed); + WriteByte(ms, (byte)source.GasPriceTolerance); + break; + case PricingModeType.Reserved when + source.Receipt != null: + ms.WriteByte((byte)PricingModeType.Reserved); + WriteBytes(ms, Hex.Decode(source.Receipt)); + break; } return ms.ToArray(); diff --git a/Casper.Network.SDK/ICasperClient.cs b/Casper.Network.SDK/ICasperClient.cs index aaa9713..b353429 100644 --- a/Casper.Network.SDK/ICasperClient.cs +++ b/Casper.Network.SDK/ICasperClient.cs @@ -90,9 +90,15 @@ Task> GetTransaction(TransactionHash transacti bool finalizedApprovals = false, CancellationToken cancellationToken = default(CancellationToken)); - Task> GetTransaction(string version1Hash, + Task> GetTransaction(TransactionHash transactionHash, + CancellationToken cancellationToken = default(CancellationToken)); + + Task> GetTransaction(string transactionV1Hash, bool finalizedApprovals = false, CancellationToken cancellationToken = default(CancellationToken)); + + Task> GetTransaction(string transactionV1Hash, + CancellationToken cancellationToken = default(CancellationToken)); Task> GetBlock(string blockHash = null); diff --git a/Casper.Network.SDK/JsonRpc/CasperMethods.cs b/Casper.Network.SDK/JsonRpc/CasperMethods.cs index 846d822..9adb5b1 100644 --- a/Casper.Network.SDK/JsonRpc/CasperMethods.cs +++ b/Casper.Network.SDK/JsonRpc/CasperMethods.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using Casper.Network.SDK.JsonRpc; using Casper.Network.SDK.Types; -using Org.BouncyCastle.Utilities.Encoders; namespace Casper.Network.SDK.JsonRpc { @@ -294,37 +292,40 @@ public class PutTransaction : RpcMethod /// /// Sends a Transaction to the network for its execution. /// - /// The deploy object. + /// The Transactionv1 object. public PutTransaction(TransactionV1 transaction) : base("account_put_transaction") { + var txParameter = new Dictionary(); + txParameter.Add("Version1", transaction); + this.Parameters = new Dictionary { { - "transaction", new Dictionary - { - { "Version1", transaction}, - } + "transaction", txParameter } }; } - } - public class GetTransaction : RpcMethod - { - public GetTransaction(string deployHash, bool finalizedApprovals = false) : base("info_get_transaction") + + /// + /// Sends a Transaction to the network for its execution. + /// + /// The Deploy object. + public PutTransaction(Deploy transaction) : base("account_put_transaction") { + var txParameter = new Dictionary(); + txParameter.Add("Deploy", transaction); + this.Parameters = new Dictionary { { - "transaction_hash", new Dictionary - { - { "Deploy", deployHash } - } - }, + "transaction", txParameter + } }; - if (finalizedApprovals) - this.Parameters.Add("finalized_approvals", true); } - + } + + public class GetTransaction : RpcMethod + { public GetTransaction(TransactionHash transactionHash, bool finalizedApprovals = false) : base("info_get_transaction") { var hashDict = new Dictionary(); diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetTransactionResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetTransactionResult.cs index a2c493a..adc9a59 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetTransactionResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetTransactionResult.cs @@ -12,6 +12,7 @@ public class GetTransactionResult : RpcResult /// The deploy. /// [JsonPropertyName("transaction")] + [JsonConverter(typeof(Transaction.TransactionConverter))] public Transaction Transaction { get; init; } /// diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index 0a68507..aa98a9d 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -473,6 +473,25 @@ public async Task> PutTransaction(TransactionV return await SendRpcRequestAsync(method); } + /// + /// Send a Transaction to the network for its execution. + /// + /// The transaction object. + /// Throws an exception if the transaction is not signed. + public async Task> PutTransaction(Transaction transaction) + { + RpcMethod method; + + if (transaction.Approvals.Count == 0) + throw new Exception("Sign the transaction before sending it to the network."); + + if(transaction.Version == TransactionVersion.Deploy) + method = new PutTransaction((Deploy)transaction); + else + method = new PutTransaction((TransactionV1)transaction); + return await SendRpcRequestAsync(method); + } + /// /// Request a Transaction object from the network by the transaction (or deploy) hash. /// When a cancellation token is included this method waits until the transaction is @@ -503,23 +522,54 @@ public async Task> GetTransaction(TransactionH throw new TaskCanceledException("GetDeploy operation canceled"); } + /// + /// Request a Transaction object from the network by the transaction (or deploy) hash. + /// When a cancellation token is included this method waits until the transaction is + /// executed, i.e. until the transaction contains the execution result information. + /// + /// An v1 transaction hash or a deploy hash + /// A CancellationToken. Do not include this parameter to return + /// with the first transaction object returned by the network, even it's not executed. + /// The token has cancelled the operation before the deploy has been executed. + public async Task> GetTransaction(TransactionHash transactionHash, + CancellationToken cancellationToken = default(CancellationToken)) + { + return await GetTransaction(transactionHash, false, cancellationToken); + } + /// /// Request a Transaction object from the network by the transaction hash. /// When a cancellation token is included this method waits until the transaction is /// executed, i.e. until the transaction contains the execution result information. /// - /// A v1 transaction hash + /// A TransactionV1 hash /// Whether to return the transaction with the finalized approvals /// substituted. If `false` or omitted, returns the transaction with the approvals that were originally /// received by the node. /// A CancellationToken. Do not include this parameter to return /// with the first transaction object returned by the network, even it's not executed. /// The token has cancelled the operation before the deploy has been executed. - public async Task> GetTransaction(string version1Hash, + public async Task> GetTransaction(string transactionV1Hash, bool finalizedApprovals = false, CancellationToken cancellationToken = default(CancellationToken)) { - return await this.GetTransaction(new TransactionHash { Version1 = version1Hash }, finalizedApprovals, + return await this.GetTransaction(new TransactionHash { Version1 = transactionV1Hash }, finalizedApprovals, + cancellationToken); + } + + /// + /// Request a Transaction object from the network by the transaction hash. + /// When a cancellation token is included this method waits until the transaction is + /// executed, i.e. until the transaction contains the execution result information. + /// + /// A TransactionV1 hash + /// A CancellationToken. Do not include this parameter to return + /// with the first transaction object returned by the network, even it's not executed. + /// The token has cancelled the operation before the deploy has been executed. + public async Task> GetTransaction(string transactionV1Hash, + CancellationToken cancellationToken = default(CancellationToken)) + { + return await this.GetTransaction(new TransactionHash { Version1 = transactionV1Hash }, false, cancellationToken); } diff --git a/Casper.Network.SDK/SSE/TransactionAccepted.cs b/Casper.Network.SDK/SSE/TransactionAccepted.cs index c3d29b3..b3235f3 100644 --- a/Casper.Network.SDK/SSE/TransactionAccepted.cs +++ b/Casper.Network.SDK/SSE/TransactionAccepted.cs @@ -3,6 +3,7 @@ namespace Casper.Network.SDK.SSE { + [JsonConverter(typeof(Transaction.TransactionConverter))] public class TransactionAccepted : Transaction { } diff --git a/Casper.Network.SDK/Types/Block.cs b/Casper.Network.SDK/Types/Block.cs index b84e062..9d49db0 100644 --- a/Casper.Network.SDK/Types/Block.cs +++ b/Casper.Network.SDK/Types/Block.cs @@ -507,24 +507,46 @@ public class BlockV2 } public enum TransactionCategory { + /// /// Native mint interaction (the default). + /// Mint = 0, + /// + /// Transfer from a Deploy transaction. + /// + [Obsolete("Use Mint instead of DeployTransfer")] + DeployTransfer = 1, + /// /// Native auction interaction. + /// Auction = 1, + /// /// Install or Upgrade. + /// InstallUpgrade = 2, + /// /// A large Wasm based transaction. + /// Large = 3, + /// + /// A non-native Deploy transaction. + /// + [Obsolete("Use Large instead of DeployLarge")] + DeployLarge = 3, + /// /// A medium Wasm based transaction. + /// Medium = 4, + /// /// A small Wasm based transaction. + /// Small = 5, } public enum TransactionVersion { Deploy = 0, - Version1 = 1, + TransactionV1 = 1, } public class BlockTransaction @@ -558,7 +580,7 @@ public override List Read( { Category = category, Hash = t.Deploy != null ? t.Deploy : t.Version1, - Version = t.Deploy != null ? TransactionVersion.Deploy : TransactionVersion.Version1, + Version = t.Deploy != null ? TransactionVersion.Deploy : TransactionVersion.TransactionV1, })); } } diff --git a/Casper.Network.SDK/Types/InitiatorAddr.cs b/Casper.Network.SDK/Types/InitiatorAddr.cs index a3f7b70..41380b9 100644 --- a/Casper.Network.SDK/Types/InitiatorAddr.cs +++ b/Casper.Network.SDK/Types/InitiatorAddr.cs @@ -36,6 +36,16 @@ public InitiatorAddr(AccountHashKey accountHash) { this.AccountHash = accountHash; } + + public static InitiatorAddr FromPublicKey(PublicKey publicKey) + { + return new InitiatorAddr(publicKey); + } + + public static InitiatorAddr FromAccountHash(AccountHashKey accountHashKey) + { + return new InitiatorAddr(accountHashKey); + } public override string ToString() { diff --git a/Casper.Network.SDK/Types/Package.cs b/Casper.Network.SDK/Types/Package.cs index c142ffb..faaf973 100644 --- a/Casper.Network.SDK/Types/Package.cs +++ b/Casper.Network.SDK/Types/Package.cs @@ -78,7 +78,7 @@ public class Package public URef AccessKey { get; init; } /// - /// All versions (enabled & disabled). + /// All versions (enabled and disabled). /// [JsonPropertyName("versions")] public List Versions { get; init; } diff --git a/Casper.Network.SDK/Types/PricingMode.cs b/Casper.Network.SDK/Types/PricingMode.cs index 106c684..ededcba 100644 --- a/Casper.Network.SDK/Types/PricingMode.cs +++ b/Casper.Network.SDK/Types/PricingMode.cs @@ -38,24 +38,68 @@ public class PricingMode /// /// Payment amount. /// - public UInt64? PaymentAmount { get; set; } + public ulong PaymentAmount { get; set; } /// /// User-specified gas_price tolerance (minimum 1). This is interpreted to mean "do not include this /// transaction in a block if the current gas price is greater than this number". /// - public UInt16? GasPriceTolerance { get; set; } + public byte GasPriceTolerance { get; set; } /// /// Standard payment. /// - public bool? StandardPayment { get; set; } + public bool StandardPayment { get; set; } /// /// Pre-paid receipt in the Reserved Pricing mode. /// public string Receipt { get; init; } + /// + /// The original payment model, where the creator of the transaction + /// specifies how much they will pay, at what gas price. + /// + /// Amount in motes to pay for the transaction. + /// Defaults to 1. Gas price tolerance admitted. + /// Defaults to true. Indicates if this is a standar payment (non-standard payments are handled via wasm code). + public static PricingMode Classic(ulong paymentAmount, byte gasPriceTolerance = 1, bool standardPayment = true) + { + return new PricingMode() + { + Type = PricingModeType.Classic, + StandardPayment = standardPayment, + GasPriceTolerance = gasPriceTolerance, + PaymentAmount = paymentAmount, + }; + } + + /// + /// The cost of the transaction is determined by the cost table, per the transaction category. + /// + /// Defaults to 1. Gas price tolerance admitted. + public static PricingMode Fixed(byte gasPriceTolerance = 1) + { + return new PricingMode() + { + Type = PricingModeType.Fixed, + GasPriceTolerance = gasPriceTolerance, + }; + } + + /// + /// The payment for this transaction was previously reserved, as proven by the receipt hash. + /// + /// Pre-paid receipt. + public static PricingMode Reserved(string receipt) + { + return new PricingMode() + { + Type = PricingModeType.Reserved, + Receipt = receipt, + }; + } + public class PricingModeConverter : JsonConverter { public override PricingMode Read( @@ -77,9 +121,9 @@ public override PricingMode Read( throw new JsonException("Cannot deserialize PricingMode. StartObject expected"); reader.Read(); - UInt64? paymentAmount = null; - UInt16? gasPriceTolerance = null; - bool? standardPayment = null; + ulong paymentAmount = 0; + byte gasPriceTolerance = 0; + bool standardPayment = false; string receipt = null; while (reader.TokenType == JsonTokenType.PropertyName) @@ -95,7 +139,7 @@ public override PricingMode Read( standardPayment = reader.GetBoolean(); break; case "gas_price_tolerance": - gasPriceTolerance = reader.GetUInt16(); + gasPriceTolerance = reader.GetByte(); break; case "receipt": receipt = reader.GetString(); @@ -127,12 +171,9 @@ public override void Write( writer.WriteStartObject(); writer.WritePropertyName("Classic"); writer.WriteStartObject(); - if (value.PaymentAmount.HasValue) - writer.WriteNumber("payment_amount", value.PaymentAmount.Value); - if (value.GasPriceTolerance.HasValue) - writer.WriteNumber("gas_price_tolerance", value.GasPriceTolerance.Value); - if (value.StandardPayment.HasValue) - writer.WriteBoolean("standard_payment", value.StandardPayment.Value); + writer.WriteNumber("payment_amount", value.PaymentAmount); + writer.WriteNumber("gas_price_tolerance", value.GasPriceTolerance); + writer.WriteBoolean("standard_payment", value.StandardPayment); writer.WriteEndObject(); writer.WriteEndObject(); } @@ -141,8 +182,7 @@ public override void Write( writer.WriteStartObject(); writer.WritePropertyName("Fixed"); writer.WriteStartObject(); - if (value.GasPriceTolerance.HasValue) - writer.WriteNumber("gas_price_tolerance", value.GasPriceTolerance.Value); + writer.WriteNumber("gas_price_tolerance", value.GasPriceTolerance); writer.WriteEndObject(); writer.WriteEndObject(); } diff --git a/Casper.Network.SDK/Types/Transaction.cs b/Casper.Network.SDK/Types/Transaction.cs index 9241aea..533aa13 100644 --- a/Casper.Network.SDK/Types/Transaction.cs +++ b/Casper.Network.SDK/Types/Transaction.cs @@ -1,22 +1,356 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; using System.Text.Json.Serialization; namespace Casper.Network.SDK.Types { - /// - /// A versioned wrapper for a transaction or deploy. - /// - public class Transaction + using ITransactionScheduling = ITransactionV1Scheduling; + + internal class TransactionCompat { /// /// The deploy. /// [JsonPropertyName("Deploy")] public Deploy Deploy { get; init; } - + /// /// A version 1 transaction. /// [JsonPropertyName("Version1")] - public TransactionV1 TransactionV1 { get; init; } + public TransactionV1 Version1 { get; init; } + } + + /// + /// A versioned wrapper for a TransactionV1 or Deploy. + /// + public class Transaction + { + protected TransactionVersion _version; + + /// + /// Returns the version of the transaction (0=Deploy, 1=TransactionV1) . + /// + public TransactionVersion Version + { + get { return _version; } + } + + private Deploy _deploy; + + private TransactionV1 _transactionV1; + + public string Hash { get; init; } + + /// + /// The address of the initiator of a transaction. + /// + public InitiatorAddr InitiatorAddr { get; set; } + + /// + /// Timestamp formatted as per RFC 3339 + /// + public ulong Timestamp { get; set; } + + /// + /// Duration of the Deploy in milliseconds (from timestamp). + /// + public ulong Ttl { get; set; } + + /// + /// Name of the chain where the deploy is executed. + /// + public string ChainName { get; set; } + + /// + /// List of signers and signatures for this transaction. + /// + public List Approvals { get; init; } + + /// + /// Pricing mode of a Transaction. + /// + public PricingMode PricingMode { get; set; } + + public ITransactionScheduling Scheduling { get; set; } + + public TransactionCategory Category { get; set; } + + public ITransactionInvocation Invocation { get; init; } + + public interface ITransactionInvocation + { + TransactionCategory Category { get; init; } + + List RuntimeArgs { get; init; } + + CLValue GetRuntimeArgValue(string name); + } + + public abstract class TransactionInvocation : ITransactionInvocation + { + public TransactionCategory Category { get; init; } + + public List RuntimeArgs { get; init; } + + public CLValue GetRuntimeArgValue(string name) + { + var runtimeArg = RuntimeArgs.FirstOrDefault(a => a.Name.Equals(name)); + return runtimeArg?.Value; + } + } + + public class NativeTransactionInvocation : TransactionInvocation + { + public NativeEntryPoint Type { get; init; } + + public string Name { get { return Type.ToString(); } } + } + + public class StoredTransactionInvocation : TransactionInvocation + { + public IInvocationTarget InvocationTarget { get; init; } + + public string EntryPoint { get; init; } + } + + public class SessionTransactionInvocation : TransactionInvocation + { + public byte[] Wasm { get; init; } + } + + public class TransactionConverter : JsonConverter + { + public override Transaction Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + var compat = JsonSerializer.Deserialize(ref reader, options); + + if (compat != null && compat.Version1 != null) + { + return (Transaction)compat.Version1; + } + + if (compat != null && compat.Deploy != null) + { + return (Transaction)compat.Deploy; + } + + throw new JsonException("Cannot deserialize Transaction. Deploy or TransactionV1 not found"); + } + catch (Exception e) + { + throw new JsonException("Cannot deserialize Transaction. " + e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + Transaction value, + JsonSerializerOptions options) + { + if (value._deploy is null && value._transactionV1 is null) + throw new JsonException("Cannot serialize empty Transaction. Deploy or TransactionV1 not found"); + + var compat = new TransactionCompat() + { + Deploy = value._deploy, + Version1 = value._transactionV1, + }; + + JsonSerializer.Serialize(writer, compat, options); + } + } + + public static explicit operator Transaction(Deploy deploy) + { + ITransactionInvocation invocation; + if (deploy.Session is TransferDeployItem transferDeployItem) + { + invocation = new NativeTransactionInvocation() + { + Type = NativeEntryPoint.Transfer, + RuntimeArgs = transferDeployItem.RuntimeArgs, + }; + } + else if (deploy.Session is ModuleBytesDeployItem moduleBytesDeployItem) + { + invocation = new SessionTransactionInvocation() + { + Wasm = moduleBytesDeployItem.ModuleBytes, + RuntimeArgs = moduleBytesDeployItem.RuntimeArgs, + }; + } + else if (deploy.Session is StoredContractByHashDeployItem storedContractByHash) + { + invocation = new StoredTransactionInvocation() + { + InvocationTarget = new ByHashInvocationTarget { Hash = storedContractByHash.Hash }, + EntryPoint = storedContractByHash.EntryPoint, + RuntimeArgs = storedContractByHash.RuntimeArgs, + }; + } + else if (deploy.Session is StoredContractByNameDeployItem storedContractByName) + { + invocation = new StoredTransactionInvocation() + { + InvocationTarget = new ByNameInvocationTarget { Name = storedContractByName.Name }, + EntryPoint = storedContractByName.EntryPoint, + RuntimeArgs = storedContractByName.RuntimeArgs, + }; + } + else if (deploy.Session is StoredVersionedContractByHashDeployItem storedVersionedContractByHash) + { + invocation = new StoredTransactionInvocation() + { + InvocationTarget = new ByPackageHashInvocationTarget + { + Hash = storedVersionedContractByHash.Hash, + Version = storedVersionedContractByHash.Version + }, + EntryPoint = storedVersionedContractByHash.EntryPoint, + RuntimeArgs = storedVersionedContractByHash.RuntimeArgs, + }; + } + else if (deploy.Session is StoredVersionedContractByNameDeployItem storedVersionedContractByName) + { + invocation = new StoredTransactionInvocation() + { + InvocationTarget = new ByPackageNameInvocationTarget + { + Name = storedVersionedContractByName.Name, + Version = storedVersionedContractByName.Version + }, + EntryPoint = storedVersionedContractByName.EntryPoint, + RuntimeArgs = storedVersionedContractByName.RuntimeArgs, + }; + } + else + { + throw new ArgumentException("No valid session object in the deploy to convert to Transaction"); + } + + PricingMode pricingMode; + if (deploy.Payment is ModuleBytesDeployItem paymentModule) + { + var amountArg = paymentModule.RuntimeArgs.FirstOrDefault(arg => arg.Name == "amount"); + if (amountArg == null) + pricingMode = new PricingMode() + { + Type = PricingModeType.Classic, + StandardPayment = false, + }; + else + { + var paymentAmount = amountArg.Value.ToBigInteger(); + pricingMode = new PricingMode() + { + Type = PricingModeType.Classic, + StandardPayment = paymentModule.ModuleBytes == null, + PaymentAmount = (ulong)paymentAmount, + }; + } + + } + else + { + throw new ArgumentException("No valid payment object in the deploy to convert to Transaction"); + } + + return new Transaction() + { + _deploy = deploy, + _version = TransactionVersion.Deploy, + Hash = deploy.Hash, + InitiatorAddr = InitiatorAddr.FromPublicKey(deploy.Header.Account), + Timestamp = deploy.Header.Timestamp, + Ttl = deploy.Header.Ttl, + ChainName = deploy.Header.ChainName, + PricingMode = pricingMode, + Approvals = deploy.Approvals, + Scheduling = TransactionScheduling.Standard, + Category = deploy.Session is TransferDeployItem ? TransactionCategory.Mint : TransactionCategory.Large, + Invocation = invocation, + }; + } + + public static explicit operator Transaction(TransactionV1 transactionV1) + { + ITransactionInvocation transactionInvocation; + + if(transactionV1.Body.Target is NativeTransactionV1Target && + transactionV1.Body.EntryPoint is NativeTransactionV1EntryPoint nativeEntryPoint) + { + transactionInvocation = new NativeTransactionInvocation() + { + Type = nativeEntryPoint.Type, + RuntimeArgs = transactionV1.Body.RuntimeArgs, + Category = transactionV1.Body.Category, + }; + } + else if (transactionV1.Body.Target is StoredTransactionV1Target storedTarget && + transactionV1.Body.EntryPoint is CustomTransactionV1EntryPoint customEntryPoint) + { + transactionInvocation = new StoredTransactionInvocation() + { + InvocationTarget = storedTarget.Id, + EntryPoint = customEntryPoint.Name, + RuntimeArgs = transactionV1.Body.RuntimeArgs, + Category = transactionV1.Body.Category, + }; + } + else if (transactionV1.Body.Target is SessionTransactionV1Target sessionTarget && + transactionV1.Body.EntryPoint.Name.Equals("Call", StringComparison.InvariantCultureIgnoreCase)) + { + transactionInvocation = new SessionTransactionInvocation() + { + Wasm = sessionTarget.ModuleBytes, + RuntimeArgs = transactionV1.Body.RuntimeArgs, + Category = transactionV1.Body.Category, + }; + } + else + { + throw new Exception("Invalid Target or EntryPoint data, or not compatible."); + } + + return new Transaction() + { + _transactionV1 = transactionV1, + _version = TransactionVersion.TransactionV1, + Hash = transactionV1.Hash, + InitiatorAddr = transactionV1.Header.InitiatorAddr, + Timestamp = transactionV1.Header.Timestamp, + Ttl = transactionV1.Header.Ttl, + ChainName = transactionV1.Header.ChainName, + PricingMode = transactionV1.Header.PricingMode, + Approvals = transactionV1.Approvals, + Scheduling = transactionV1.Body.Scheduling, + Category = transactionV1.Body.Category, + Invocation = transactionInvocation, + }; + } + + public static explicit operator Deploy(Transaction transaction) + { + if (transaction._version == TransactionVersion.Deploy) + return transaction._deploy; + + throw new InvalidCastException("TransactionV1 transaction cannot be converted to Deploy"); + } + + public static explicit operator TransactionV1(Transaction transaction) + { + if (transaction._version == TransactionVersion.TransactionV1) + return transaction._transactionV1; + + throw new InvalidCastException("Deploy transaction cannot be converted to TransactionV1"); + } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionEntryPoint.cs b/Casper.Network.SDK/Types/TransactionEntryPoint.cs deleted file mode 100644 index fca8818..0000000 --- a/Casper.Network.SDK/Types/TransactionEntryPoint.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Casper.Network.SDK.Types -{ - public enum NativeEntryPoint - { - /// - /// The `transfer` native entry point, used to transfer `Motes` from a source purse to a target purse. - /// - Transfer = 1, - /// - /// The `add_bid` native entry point, used to create or top off a bid purse. - /// - AddBid = 2, - /// - /// The `withdraw_bid` native entry point, used to decrease a stake. - /// - WithdrawBid = 3, - /// - /// The `delegate` native entry point, used to add a new delegator or increase an existing delegator's stake. - /// - Delegate = 4, - /// - /// The `undelegate` native entry point, used to reduce a delegator's stake or remove the delegator if the remaining stake is 0. - /// - Undelegate = 5, - /// - /// The `redelegate` native entry point, used to reduce a delegator's stake or remove the delegator if - /// the remaining stake is 0, and after the unbonding delay, automatically delegate to a new validator. - /// - Redelegate = 6, - /// - /// The `activate_bid` native entry point, used to used to reactivate an inactive bid. - /// - ActivateBid = 7, - /// - /// The `change_bid_public_key` native entry point, used to change a bid's public key. - /// - ChangeBidPublicKey = 8, - /// - /// Used to call entry point call() in session transactions - /// - Call = 9, - } - - public class TransactionEntryPoint - { - public NativeEntryPoint? Native { get; init; } - - public string Custom { get; init; } - - public TransactionEntryPoint(NativeEntryPoint name) - { - Native = name; - Custom = null; - } - - public TransactionEntryPoint(string customEntryPoint) - { - Native = null; - Custom = customEntryPoint; - } - - public class TransactionEntryPointConverter : JsonConverter - { - public override TransactionEntryPoint Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.String) - { - var nativeEntryPoint = EnumCompat.Parse(reader.GetString()); - return new TransactionEntryPoint(nativeEntryPoint); - } - - if (reader.TokenType == JsonTokenType.StartObject) - { - reader.Read(); - if (reader.TokenType == JsonTokenType.PropertyName && - reader.GetString() == "Custom") - { - reader.Read(); - var customEntryPoint = reader.GetString(); - reader.Read(); - return new TransactionEntryPoint(customEntryPoint); - } - } - - throw new JsonException("Cannot deserialize TransactionEntryPoint."); - } - - public override void Write( - Utf8JsonWriter writer, - TransactionEntryPoint value, - JsonSerializerOptions options) - { - if (value.Native.HasValue) - { - writer.WriteStringValue(value.Native.Value.ToString()); - } - else if (value.Custom != null) - { - writer.WriteStartObject(); - writer.WritePropertyName("Custom"); - writer.WriteStringValue(value.Custom); - writer.WriteEndObject(); - } - else - throw new JsonException("Cannot serialize empty transaction entry point."); - } - } - } -} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionScheduling.cs b/Casper.Network.SDK/Types/TransactionScheduling.cs deleted file mode 100644 index c6ea9b6..0000000 --- a/Casper.Network.SDK/Types/TransactionScheduling.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; -using Casper.Network.SDK.Utils; - -namespace Casper.Network.SDK.Types -{ - public enum TransactionSchedulingType - { - Standard = 0, - FutureEra = 1, - FutureTimestamp = 2, - } - - public class TransactionScheduling - { - public TransactionSchedulingType Type { get; init; } - - public ulong EraId { get; init; } - - public ulong Timestamp { get; init; } - - public class TransactionSchedulingConverter : JsonConverter - { - public override TransactionScheduling Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - TransactionScheduling scheduling; - - if (reader.TokenType == JsonTokenType.String) - { - var schedulingType = reader.GetString(); - switch (schedulingType) - { - case "Standard": - scheduling = new TransactionScheduling - { - Type = TransactionSchedulingType.Standard, - }; - break; - default: - throw new JsonException("Cannot deserialize TransactionScheduling. Unknown scheduling type"); - } - } - else if (reader.TokenType == JsonTokenType.StartObject) - { - reader.Read(); // skip start object - var schedulingType = reader.GetString(); - reader.Read(); - switch (schedulingType) - { - case "FutureEra": - scheduling = new TransactionScheduling - { - Type = TransactionSchedulingType.FutureEra, - EraId = reader.GetUInt64(), - }; - break; - case "FutureTimestamp": - scheduling = new TransactionScheduling - { - Type = TransactionSchedulingType.FutureTimestamp, - Timestamp = reader.GetUInt64(), - }; - break; - default: - throw new JsonException("Cannot deserialize TransactionScheduling. Unknown scheduling type"); - } - reader.Read(); - } - else - throw new JsonException("Cannot deserialize TransactionScheduling."); - - return scheduling; - } - - public override void Write( - Utf8JsonWriter writer, - TransactionScheduling value, - JsonSerializerOptions options) - { - switch (value.Type) - { - case TransactionSchedulingType.Standard: - writer.WriteStringValue("Standard"); - break; - case TransactionSchedulingType.FutureEra: - writer.WriteStartObject(); - writer.WriteNumber("FutureEra", value.EraId); - writer.WriteEndObject(); - break; - case TransactionSchedulingType.FutureTimestamp: - writer.WriteStartObject(); - writer.WriteString("FutureTimestamp", DateUtils.ToISOString(value.Timestamp)); - writer.WriteEndObject(); - break; - default: - throw new JsonException("Cannot serialize due to unkown transaction scheduling type."); - } - } - } - } -} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionV1Body.cs b/Casper.Network.SDK/Types/TransactionV1Body.cs index 65d4a38..0cb5c43 100644 --- a/Casper.Network.SDK/Types/TransactionV1Body.cs +++ b/Casper.Network.SDK/Types/TransactionV1Body.cs @@ -20,24 +20,24 @@ public class TransactionV1Body /// Entry point or method of the contract to call. /// [JsonPropertyName("entry_point")] - [JsonConverter(typeof(TransactionEntryPoint.TransactionEntryPointConverter))] - public TransactionEntryPoint EntryPoint { get; init; } + [JsonConverter(typeof(TransactionV1EntryPoint.TransactionEntryPointConverter))] + public ITransactionV1EntryPoint EntryPoint { get; init; } /// - /// Target of the transaction (native, custom or module_bytes). + /// Target contract of the transaction (native, custom or session). /// [JsonPropertyName("target")] - [JsonConverter(typeof(TransactionTarget.TransactionTargetConverter))] - public TransactionTarget Target { get; init; } + [JsonConverter(typeof(TransactionV1Target.TransactionTargetConverter))] + public ITransactionV1Target Target { get; init; } /// - /// Scheduling of the transaction.. + /// Scheduling of the transaction. /// [JsonPropertyName("scheduling")] - [JsonConverter(typeof(TransactionScheduling.TransactionSchedulingConverter))] - public TransactionScheduling Scheduling { get; init; } + [JsonConverter(typeof(TransactionV1Scheduling.TransactionV1SchedulingConverter))] + public ITransactionV1Scheduling Scheduling { get; init; } [JsonPropertyName("transaction_category")] - public byte TransactionCategory { get; init; } + public TransactionCategory Category { get; init; } } } diff --git a/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs b/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs new file mode 100644 index 0000000..b3aceb3 --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs @@ -0,0 +1,184 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + /// + /// Collection of entry points available for native calls to the system contracts. + /// + public enum NativeEntryPoint + { + /// + /// The `transfer` native entry point, used to transfer `Motes` from a source purse to a target purse. + /// + Transfer = 1, + + /// + /// The `add_bid` native entry point, used to create or top off a bid purse. + /// + AddBid = 2, + + /// + /// The `withdraw_bid` native entry point, used to decrease a stake. + /// + WithdrawBid = 3, + + /// + /// The `delegate` native entry point, used to add a new delegator or increase an existing delegator's stake. + /// + Delegate = 4, + + /// + /// The `undelegate` native entry point, used to reduce a delegator's stake or remove the delegator if the remaining stake is 0. + /// + Undelegate = 5, + + /// + /// The `redelegate` native entry point, used to reduce a delegator's stake or remove the delegator if + /// the remaining stake is 0, and after the unbonding delay, automatically delegate to a new validator. + /// + Redelegate = 6, + + /// + /// The `activate_bid` native entry point, used to used to reactivate an inactive bid. + /// + ActivateBid = 7, + + /// + /// The `change_bid_public_key` native entry point, used to change a bid's public key. + /// + ChangeBidPublicKey = 8, + + /// + /// Used to call entry point call() in session transactions + /// + Call = 9, + } + + + public interface ITransactionV1EntryPoint + { + public string Name { get; } + } + + public class NativeTransactionV1EntryPoint : ITransactionV1EntryPoint + { + public NativeEntryPoint Type { get; init; } + + public string Name + { + get { return Type.ToString(); } + } + + public NativeTransactionV1EntryPoint(NativeEntryPoint type) + { + Type = type; + } + + public NativeTransactionV1EntryPoint(string name) + { + try + { + var nativeEntryPoint = EnumCompat.Parse(name); + Type = nativeEntryPoint; + } + catch (Exception e) + { + throw new Exception($"Invalid name for a TransactionV1NativeEntryPoint ({name})."); + } + } + } + + public class CustomTransactionV1EntryPoint : ITransactionV1EntryPoint + { + public string Name { get; init; } + + public CustomTransactionV1EntryPoint(string name) + { + Name = name; + } + } + + /// + /// A native or custom entry point to call within a transaction. + /// + public class TransactionV1EntryPoint + { + public static ITransactionV1EntryPoint Transfer => new NativeTransactionV1EntryPoint(NativeEntryPoint.Transfer); + + public static ITransactionV1EntryPoint AddBid => + new NativeTransactionV1EntryPoint(NativeEntryPoint.AddBid); + + public static ITransactionV1EntryPoint WithdrawBid => + new NativeTransactionV1EntryPoint(NativeEntryPoint.WithdrawBid); + + public static ITransactionV1EntryPoint Delegate => + new NativeTransactionV1EntryPoint(NativeEntryPoint.Delegate); + + public static ITransactionV1EntryPoint Undelegate => + new NativeTransactionV1EntryPoint(NativeEntryPoint.Undelegate); + + public static ITransactionV1EntryPoint Redelegate => + new NativeTransactionV1EntryPoint(NativeEntryPoint.Redelegate); + + public static ITransactionV1EntryPoint ActivateBid => + new NativeTransactionV1EntryPoint(NativeEntryPoint.ActivateBid); + + public static ITransactionV1EntryPoint ChangeBidPublicKey => + new NativeTransactionV1EntryPoint(NativeEntryPoint.ChangeBidPublicKey); + + public static ITransactionV1EntryPoint Call => + new NativeTransactionV1EntryPoint(NativeEntryPoint.Call); + + public class TransactionEntryPointConverter : JsonConverter + { + public override ITransactionV1EntryPoint Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + return new NativeTransactionV1EntryPoint(reader.GetString()); + } + + if (reader.TokenType == JsonTokenType.StartObject) + { + reader.Read(); + if (reader.TokenType == JsonTokenType.PropertyName && + reader.GetString() == "Custom") + { + reader.Read(); + var customEntryPoint = reader.GetString(); + reader.Read(); + return new CustomTransactionV1EntryPoint(customEntryPoint); + } + } + + throw new JsonException("Cannot deserialize TransactionEntryPoint."); + } + + public override void Write( + Utf8JsonWriter writer, + ITransactionV1EntryPoint value, + JsonSerializerOptions options) + { + switch (value) + { + case NativeTransactionV1EntryPoint nativeEntryPoint: + writer.WriteStringValue(nativeEntryPoint.Name); + break; + case CustomTransactionV1EntryPoint customEntryPoint: + writer.WriteStartObject(); + writer.WritePropertyName("Custom"); + writer.WriteStringValue(customEntryPoint.Name); + writer.WriteEndObject(); + break; + default: + throw new JsonException("Cannot serialize empty transaction entry point."); + } + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionV1Header.cs b/Casper.Network.SDK/Types/TransactionV1Header.cs index 8b30fdc..d1418ff 100644 --- a/Casper.Network.SDK/Types/TransactionV1Header.cs +++ b/Casper.Network.SDK/Types/TransactionV1Header.cs @@ -49,5 +49,11 @@ public class TransactionV1Header /// [JsonPropertyName("chain_name")] public string ChainName { get; set; } + + public TransactionV1Header() + { + Timestamp = DateUtils.ToEpochTime(DateTime.UtcNow); + Ttl = 1800000; + } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionV1Scheduling.cs b/Casper.Network.SDK/Types/TransactionV1Scheduling.cs new file mode 100644 index 0000000..2c4281b --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionV1Scheduling.cs @@ -0,0 +1,149 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Utils; + +namespace Casper.Network.SDK.Types +{ + public enum TransactionV1SchedulingType + { + Standard = 0, + FutureEra = 1, + FutureTimestamp = 2, + } + + public interface ITransactionV1Scheduling + { + } + + public class StandardTransactionScheduling : ITransactionV1Scheduling + { + } + + public class StandardTransactionV1Scheduling : StandardTransactionScheduling + { + } + + public class FutureEraTransactionScheduling : ITransactionV1Scheduling + { + public ulong EraId { get; init; } + } + + public class FutureEraTransactionV1Scheduling : FutureEraTransactionScheduling + { + } + + public class FutureTimestampTransactionScheduling : ITransactionV1Scheduling + { + public ulong Timestamp { get; init; } + } + + public class FutureTimestampTransactionV1Scheduling : FutureTimestampTransactionScheduling + { + } + + public abstract class TransactionScheduling + { + public static ITransactionV1Scheduling Standard => + new StandardTransactionV1Scheduling(); + + public static ITransactionV1Scheduling FutureEra(ulong eraId) + { + return new FutureEraTransactionV1Scheduling() + { + EraId = eraId, + }; + } + + public static ITransactionV1Scheduling FutureTimestamp(ulong timestamp) + { + return new FutureTimestampTransactionV1Scheduling() + { + Timestamp = timestamp, + }; + } + } + + public class TransactionV1Scheduling : TransactionScheduling + { + public class TransactionV1SchedulingConverter : JsonConverter + { + public override ITransactionV1Scheduling Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + ITransactionV1Scheduling v1Scheduling; + + if (reader.TokenType == JsonTokenType.String) + { + var schedulingType = reader.GetString(); + switch (schedulingType) + { + case "Standard": + v1Scheduling = new StandardTransactionV1Scheduling(); + break; + default: + throw new JsonException( + "Cannot deserialize TransactionScheduling. Unknown scheduling type"); + } + } + else if (reader.TokenType == JsonTokenType.StartObject) + { + reader.Read(); // skip start object + var schedulingType = reader.GetString(); + reader.Read(); + switch (schedulingType) + { + case "FutureEra": + v1Scheduling = new FutureEraTransactionV1Scheduling + { + EraId = reader.GetUInt64(), + }; + break; + case "FutureTimestamp": + v1Scheduling = new FutureTimestampTransactionV1Scheduling + { + Timestamp = DateUtils.ToEpochTime(reader.GetString()), + }; + break; + default: + throw new JsonException( + "Cannot deserialize TransactionScheduling. Unknown scheduling type"); + } + + reader.Read(); + } + else + throw new JsonException("Cannot deserialize TransactionScheduling."); + + return v1Scheduling; + } + + public override void Write( + Utf8JsonWriter writer, + ITransactionV1Scheduling value, + JsonSerializerOptions options) + { + switch (value) + { + case StandardTransactionV1Scheduling: + writer.WriteStringValue("Standard"); + break; + case FutureEraTransactionV1Scheduling eraScheduling: + writer.WriteStartObject(); + writer.WriteNumber("FutureEra", eraScheduling.EraId); + writer.WriteEndObject(); + break; + case FutureTimestampTransactionV1Scheduling timestampScheduling: + writer.WriteStartObject(); + writer.WriteString("FutureTimestamp", DateUtils.ToISOString(timestampScheduling.Timestamp)); + writer.WriteEndObject(); + break; + default: + throw new JsonException("Cannot serialize due to unkown transaction scheduling type."); + } + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionTarget.cs b/Casper.Network.SDK/Types/TransactionV1Target.cs similarity index 74% rename from Casper.Network.SDK/Types/TransactionTarget.cs rename to Casper.Network.SDK/Types/TransactionV1Target.cs index 31cc88e..85714f4 100644 --- a/Casper.Network.SDK/Types/TransactionTarget.cs +++ b/Casper.Network.SDK/Types/TransactionV1Target.cs @@ -2,7 +2,6 @@ using System.Text.Json; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; -using Org.BouncyCastle.Tls.Crypto.Impl.BC; using Org.BouncyCastle.Utilities.Encoders; namespace Casper.Network.SDK.Types @@ -27,7 +26,7 @@ public class ByNameInvocationTarget : IInvocationTarget public class ByPackageHashInvocationTarget : IInvocationTarget { - [JsonPropertyName("addr")] public string Addr { get; init; } + [JsonPropertyName("addr")] public string Hash { get; init; } [JsonPropertyName("version")] public UInt32? Version { get; init; } } @@ -134,69 +133,89 @@ public enum TransactionRuntime VmCasperV2, } - public class TransactionTarget + public interface ITransactionV1Target { - public TransactionTargetType Type { get; init; } + } + + public class NativeTransactionV1Target : ITransactionV1Target + { + } - [JsonPropertyName("id")] public IInvocationTarget Id { get; init; } + public class StoredTransactionV1Target : ITransactionV1Target + { + [JsonPropertyName("id")] + public IInvocationTarget Id { get; init; } /// - /// wasm Bytes + /// Targeted Casper VM version. + /// + [JsonPropertyName("runtime")] + public TransactionRuntime Runtime { get; set; } + } + + public class SessionTransactionV1Target : ITransactionV1Target + { + /// + /// Wasm bytes for a Session transaction type. /// [JsonPropertyName("module_bytes")] [JsonConverter(typeof(HexBytesConverter))] public byte[] ModuleBytes { get; init; } - - [JsonPropertyName("runtime")] public TransactionRuntime Runtime { get; set; } - - public static TransactionTarget StoredByHash(string hash) + + /// + /// Targeted Casper VM version. + /// + [JsonPropertyName("runtime")] + public TransactionRuntime Runtime { get; set; } + } + + public class TransactionV1Target + { + public static ITransactionV1Target Native => new NativeTransactionV1Target(); + + public static ITransactionV1Target StoredByHash(string contractHash) { - return new TransactionTarget() + return new StoredTransactionV1Target() { - Type = TransactionTargetType.Stored, - Id = new ByHashInvocationTarget { Hash = hash } + Id = new ByHashInvocationTarget { Hash = contractHash } }; } - public static TransactionTarget StoredByName(string name) + public static ITransactionV1Target StoredByName(string name) { - return new TransactionTarget() + return new StoredTransactionV1Target() { - Type = TransactionTargetType.Stored, Id = new ByNameInvocationTarget { Name = name } }; } - public static TransactionTarget StoredByPackageHash(string hash, UInt32? version = null) + public static ITransactionV1Target StoredByPackageHash(string packageHash, UInt32? version = null) { - return new TransactionTarget() + return new StoredTransactionV1Target() { - Type = TransactionTargetType.Stored, - Id = new ByPackageHashInvocationTarget { Addr = hash, Version = version } + Id = new ByPackageHashInvocationTarget { Hash = packageHash, Version = version } }; } - public static TransactionTarget StoredByPackageName(string name, UInt32? version = null) + public static ITransactionV1Target StoredByPackageName(string name, UInt32? version = null) { - return new TransactionTarget() + return new StoredTransactionV1Target() { - Type = TransactionTargetType.Stored, Id = new ByPackageNameInvocationTarget() { Name = name, Version = version } }; } - public static TransactionTarget Session(byte[] moduleBytes) + public static ITransactionV1Target Session(byte[] moduleBytes) { - return new TransactionTarget() + return new SessionTransactionV1Target() { - Type = TransactionTargetType.Session, ModuleBytes = moduleBytes, }; } - public class TransactionTargetConverter : JsonConverter + public class TransactionTargetConverter : JsonConverter { - public override TransactionTarget Read( + public override ITransactionV1Target Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) @@ -205,25 +224,21 @@ public override TransactionTarget Read( { var targetType = reader.GetString(); - var type = EnumCompat.Parse(targetType); switch (targetType) { case "Native": - return new TransactionTarget - { - Type = TransactionTargetType.Native, - }; + return new NativeTransactionV1Target(); default: throw new JsonException($"TransactionTargetType '{targetType}' not supported."); } } - else if (reader.TokenType == JsonTokenType.StartObject) + if (reader.TokenType == JsonTokenType.StartObject) { - TransactionTarget transactionTarget = null; + ITransactionV1Target transactionTarget = null; IInvocationTarget id = null; string module_bytes = null; - string runtime = null; - + TransactionRuntime runtime = TransactionRuntime.VmCasperV1; + reader.Read(); // skip start object var targetType = reader.GetString(); reader.Read(); @@ -243,7 +258,7 @@ public override TransactionTarget Read( reader.Read(); break; case "runtime": - runtime = reader.GetString(); + runtime = EnumCompat.Parse(reader.GetString()); reader.Read(); // skip end object break; } @@ -251,13 +266,11 @@ public override TransactionTarget Read( reader.Read(); // skip end object - transactionTarget = new TransactionTarget() + transactionTarget = new StoredTransactionV1Target() { - Type = EnumCompat.Parse(targetType), Id = id, + Runtime = runtime, }; - if (runtime != null) - transactionTarget.Runtime = EnumCompat.Parse(runtime); break; case "Session": reader.Read(); @@ -271,18 +284,17 @@ public override TransactionTarget Read( module_bytes = reader.GetString(); break; case "runtime": - runtime = reader.GetString(); + runtime = EnumCompat.Parse(reader.GetString()); break; } } reader.Read(); // skip end object - - transactionTarget = new TransactionTarget() + + transactionTarget = new SessionTransactionV1Target() { - Type = EnumCompat.Parse(targetType), ModuleBytes = Hex.Decode(module_bytes), - Runtime = EnumCompat.Parse(runtime), + Runtime = runtime, }; break; default: @@ -297,28 +309,28 @@ public override TransactionTarget Read( public override void Write( Utf8JsonWriter writer, - TransactionTarget value, + ITransactionV1Target value, JsonSerializerOptions options) { - switch (value.Type) + switch (value) { - case TransactionTargetType.Native: + case NativeTransactionV1Target: writer.WriteStringValue("Native"); break; - case TransactionTargetType.Stored: + case StoredTransactionV1Target storedTarget: writer.WriteStartObject(); writer.WriteStartObject("Stored"); writer.WritePropertyName("id"); - JsonSerializer.Serialize(writer, value.Id); - writer.WriteString("runtime", value.Runtime.ToString()); + JsonSerializer.Serialize(writer, storedTarget.Id); + writer.WriteString("runtime", storedTarget.Runtime.ToString()); writer.WriteEndObject(); writer.WriteEndObject(); break; - case TransactionTargetType.Session: + case SessionTransactionV1Target sessionTarget: writer.WriteStartObject(); writer.WriteStartObject("Session"); - writer.WriteString("module_bytes", Hex.ToHexString(value.ModuleBytes)); - writer.WriteString("runtime", value.Runtime.ToString()); + writer.WriteString("module_bytes", Hex.ToHexString(sessionTarget.ModuleBytes)); + writer.WriteString("runtime", sessionTarget.Runtime.ToString()); writer.WriteEndObject(); writer.WriteEndObject(); break; diff --git a/Casper.Network.SDK/Utils/RpcResponseExtensions.cs b/Casper.Network.SDK/Utils/RpcResponseExtensions.cs index d8a832f..234fae3 100644 --- a/Casper.Network.SDK/Utils/RpcResponseExtensions.cs +++ b/Casper.Network.SDK/Utils/RpcResponseExtensions.cs @@ -1,4 +1,6 @@ +using System.Text.Json; using Casper.Network.SDK.JsonRpc; +using Casper.Network.SDK.Types; namespace Casper.Network.SDK.Utils { @@ -14,5 +16,25 @@ public static string GetDeployHash(this RpcResponse resp throw new RpcClientException("deploy_hash property not found."); } + + /// + /// Retrieves the Transaction hash from an RpcResponse object. + /// + public static TransactionHash GetTransactionHash(this RpcResponse response) + { + if (response.Result.TryGetProperty("transaction_hash", out var el)) + { + if (el.ValueKind != JsonValueKind.Null) + { + var hash = JsonSerializer.Deserialize(el.GetRawText()); + if (hash != null) + return hash; + + throw new RpcClientException("Cannot deserialize transaction_hash property in response."); + } + } + + throw new RpcClientException("transaction_hash property not found."); + } } } \ No newline at end of file From ea537fb5eb524ff105d1911ab2dd49e5b21a2e8d Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 18 Jul 2024 16:49:21 +0200 Subject: [PATCH 073/126] Bugfix: fixed parsing of Step event on Casper 1.x network Signed-off-by: David Hernando --- Casper.Network.SDK/SSE/Step.cs | 83 ++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/Casper.Network.SDK/SSE/Step.cs b/Casper.Network.SDK/SSE/Step.cs index 8fea834..f11f9f3 100644 --- a/Casper.Network.SDK/SSE/Step.cs +++ b/Casper.Network.SDK/SSE/Step.cs @@ -1,12 +1,38 @@ +using System; using System.Collections.Generic; +using System.Linq; +using System.Text.Json; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; namespace Casper.Network.SDK.Types { + internal class StepCompat + { + /// + /// The era in which the change occurred. + /// + [JsonPropertyName("era_id")] + public ulong EraId { get; init; } + + /// + /// The resulting operations (only for Casper v1.x). + /// + [JsonPropertyName("execution_effect")] + public ExecutionEffect ExecutionEffect { get; init; } + + /// + /// The effect of executing the deploy. + /// + [JsonPropertyName("execution_effects")] + [JsonConverter(typeof(GenericListConverter))] + public List Effects { get; init; } + } + /// /// The journal of execution transforms from an Era /// + [JsonConverter(typeof(StepConverter))] public class Step { /// @@ -15,11 +41,68 @@ public class Step [JsonPropertyName("era_id")] public ulong EraId { get; init; } + /// + /// The resulting operations (only for Casper v1.x). + /// + [JsonPropertyName("operations")] + public List Operations { get; init; } + /// /// The effect of executing the deploy. /// [JsonPropertyName("execution_effects")] [JsonConverter(typeof(GenericListConverter))] public List Effects { get; init; } + + public class StepConverter : JsonConverter + { + public override Step Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + var stepCompat = JsonSerializer.Deserialize(ref reader, options); + + if (stepCompat.ExecutionEffect == null) + return new Step() + { + EraId = stepCompat.EraId, + Effects = stepCompat.Effects, + }; + + return new Step() + { + EraId = stepCompat.EraId, + Operations = stepCompat.ExecutionEffect.Operations, + Effects = stepCompat.ExecutionEffect.Transforms.Select(t => (Transform)t).ToList(), + }; + } + + public override void Write( + Utf8JsonWriter writer, + Step value, + JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteNumber("era_id", value.EraId); + if (value.Operations != null && value.Operations.Count > 0) + { + writer.WritePropertyName("operations"); + JsonSerializer.Serialize(writer, value.Operations); + } + writer.WritePropertyName("execution_effects"); + + if (value.Effects == null || value.Effects.Count == 0) + { + writer.WriteStartArray(); + writer.WriteEndArray(); + } + else + { + JsonSerializer.Serialize(writer, value.Effects); + } + writer.WriteEndObject(); + } + } } } \ No newline at end of file From 8c8a313e7288e60091473ebd17bf00af8abc4f6c Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 18 Jul 2024 19:15:52 +0200 Subject: [PATCH 074/126] removed default value for finalizedApprovals Signed-off-by: David Hernando --- Casper.Network.SDK/ICasperClient.cs | 4 ++-- Casper.Network.SDK/NetCasperClient.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Casper.Network.SDK/ICasperClient.cs b/Casper.Network.SDK/ICasperClient.cs index b353429..630d4c7 100644 --- a/Casper.Network.SDK/ICasperClient.cs +++ b/Casper.Network.SDK/ICasperClient.cs @@ -87,14 +87,14 @@ Task> GetDeploy(string deployHash, Task> PutTransaction(TransactionV1 transaction); Task> GetTransaction(TransactionHash transactionHash, - bool finalizedApprovals = false, + bool finalizedApprovals, CancellationToken cancellationToken = default(CancellationToken)); Task> GetTransaction(TransactionHash transactionHash, CancellationToken cancellationToken = default(CancellationToken)); Task> GetTransaction(string transactionV1Hash, - bool finalizedApprovals = false, + bool finalizedApprovals, CancellationToken cancellationToken = default(CancellationToken)); Task> GetTransaction(string transactionV1Hash, diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index aa98a9d..abc4693 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -505,7 +505,7 @@ public async Task> PutTransaction(Transaction /// with the first transaction object returned by the network, even it's not executed. /// The token has cancelled the operation before the deploy has been executed. public async Task> GetTransaction(TransactionHash transactionHash, - bool finalizedApprovals = false, + bool finalizedApprovals, CancellationToken cancellationToken = default(CancellationToken)) { var method = new GetTransaction(transactionHash, finalizedApprovals); @@ -550,7 +550,7 @@ public async Task> GetTransaction(TransactionH /// with the first transaction object returned by the network, even it's not executed. /// The token has cancelled the operation before the deploy has been executed. public async Task> GetTransaction(string transactionV1Hash, - bool finalizedApprovals = false, + bool finalizedApprovals, CancellationToken cancellationToken = default(CancellationToken)) { return await this.GetTransaction(new TransactionHash { Version1 = transactionV1Hash }, finalizedApprovals, From 69555008c155fc2012e475627dbf4f6387a21c96 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 19 Jul 2024 10:54:56 +0200 Subject: [PATCH 075/126] Refactored PricingMode Signed-off-by: David Hernando --- .../RPCResponses/GetTransactionTest.cs | 2 + .../TransactionV1ByteSerializer.cs | 21 +- Casper.Network.SDK/Compat/EnumCompat.cs | 5 + Casper.Network.SDK/Types/PricingMode.cs | 180 +++++++++--------- Casper.Network.SDK/Types/Transaction.cs | 20 +- .../Types/TransactionV1Header.cs | 2 +- 6 files changed, 109 insertions(+), 121 deletions(-) diff --git a/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs index c6124ec..40b6173 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs @@ -99,6 +99,8 @@ public void GetTransactionWithSessionTransactionTest_v200() Assert.AreEqual("01020304", Hex.ToHexString((transaction.Invocation as Transaction.SessionTransactionInvocation)!.Wasm)); Assert.AreEqual(TransactionCategory.InstallUpgrade, transaction.Category); Assert.IsTrue(transaction.Scheduling is StandardTransactionScheduling); + Assert.IsTrue(transaction.PricingMode is FixedPricingMode); + Assert.AreEqual(2, ((FixedPricingMode)transaction.PricingMode).GasPriceTolerance); Assert.AreEqual(transactionV1.Approvals.Count, transaction.Approvals.Count); Assert.AreEqual(transactionV1.Approvals[0].Signature, transaction.Approvals[0].Signature); diff --git a/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs b/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs index 3678025..8c37d8d 100644 --- a/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs +++ b/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs @@ -121,26 +121,25 @@ public byte[] ToBytes(TransactionV1Body source) return ms.ToArray(); } - public byte[] ToBytes(PricingMode source) + public byte[] ToBytes(IPricingMode source) { var ms = new MemoryStream(); - switch (source.Type) + switch (source) { - case PricingModeType.Classic: + case ClassicPricingMode classicPricingMode: ms.WriteByte((byte)PricingModeType.Classic); - WriteULong(ms, (ulong)source.PaymentAmount); - WriteByte(ms, (byte)source.GasPriceTolerance); - WriteByte(ms, (byte)(source.StandardPayment ? 0x01 : 0x00)); + WriteULong(ms, (ulong)classicPricingMode.PaymentAmount); + WriteByte(ms, (byte)classicPricingMode.GasPriceTolerance); + WriteByte(ms, (byte)(classicPricingMode.StandardPayment ? 0x01 : 0x00)); break; - case PricingModeType.Fixed: + case FixedPricingMode fixedPricingMode: ms.WriteByte((byte)PricingModeType.Fixed); - WriteByte(ms, (byte)source.GasPriceTolerance); + WriteByte(ms, (byte)fixedPricingMode.GasPriceTolerance); break; - case PricingModeType.Reserved when - source.Receipt != null: + case ReservedPricingMode reservedPricingMode: ms.WriteByte((byte)PricingModeType.Reserved); - WriteBytes(ms, Hex.Decode(source.Receipt)); + WriteBytes(ms, Hex.Decode(reservedPricingMode.Receipt)); break; } diff --git a/Casper.Network.SDK/Compat/EnumCompat.cs b/Casper.Network.SDK/Compat/EnumCompat.cs index cae5ad5..15d902e 100644 --- a/Casper.Network.SDK/Compat/EnumCompat.cs +++ b/Casper.Network.SDK/Compat/EnumCompat.cs @@ -7,6 +7,11 @@ public static TEnum Parse(this String value) where TEnum : struct { return (TEnum)Enum.Parse(typeof(TEnum), value); } + + public static bool TryParse(this String value, out TEnum e) where TEnum : struct + { + return Enum.TryParse( value, true, out e); + } public static string GetName(TEnum value) where TEnum : Enum { diff --git a/Casper.Network.SDK/Types/PricingMode.cs b/Casper.Network.SDK/Types/PricingMode.cs index ededcba..08e454b 100644 --- a/Casper.Network.SDK/Types/PricingMode.cs +++ b/Casper.Network.SDK/Types/PricingMode.cs @@ -24,38 +24,61 @@ public enum PricingModeType Reserved = 2, } - /// - /// Pricing mode of a Transaction. - /// - public class PricingMode + public interface IPricingMode + { +#if NET7_0_OR_GREATER + public bool IsClassic => this is ClassicPricingMode; + public bool IsFixed => this is FixedPricingMode; + public bool IsReserved => this is ReservedPricingMode; +#endif + } + + public class ClassicPricingMode: IPricingMode { /// - /// Pricing mode used: Classic, Fixed, Reserved. - /// + /// Payment amount. /// - public PricingModeType Type { get; init; } - + [JsonPropertyName("payment_amount")] + public ulong PaymentAmount { get; init; } + /// - /// Payment amount. + /// Standard payment. /// - public ulong PaymentAmount { get; set; } - + [JsonPropertyName("standard_payment")] + public bool StandardPayment { get; init; } + /// /// User-specified gas_price tolerance (minimum 1). This is interpreted to mean "do not include this /// transaction in a block if the current gas price is greater than this number". /// - public byte GasPriceTolerance { get; set; } - + [JsonPropertyName("gas_price_tolerance")] + public byte GasPriceTolerance { get; init; } + } + + public class FixedPricingMode: IPricingMode + { /// - /// Standard payment. + /// User-specified gas_price tolerance (minimum 1). This is interpreted to mean "do not include this + /// transaction in a block if the current gas price is greater than this number". /// - public bool StandardPayment { get; set; } - + [JsonPropertyName("gas_price_tolerance")] + public byte GasPriceTolerance { get; init; } + } + + public class ReservedPricingMode: IPricingMode + { /// /// Pre-paid receipt in the Reserved Pricing mode. /// + [JsonPropertyName("receipt")] public string Receipt { get; init; } - + } + + /// + /// Pricing mode of a Transaction. + /// + public class PricingMode + { /// /// The original payment model, where the creator of the transaction /// specifies how much they will pay, at what gas price. @@ -63,11 +86,10 @@ public class PricingMode /// Amount in motes to pay for the transaction. /// Defaults to 1. Gas price tolerance admitted. /// Defaults to true. Indicates if this is a standar payment (non-standard payments are handled via wasm code). - public static PricingMode Classic(ulong paymentAmount, byte gasPriceTolerance = 1, bool standardPayment = true) + public static IPricingMode Classic(ulong paymentAmount, byte gasPriceTolerance = 1, bool standardPayment = true) { - return new PricingMode() + return new ClassicPricingMode() { - Type = PricingModeType.Classic, StandardPayment = standardPayment, GasPriceTolerance = gasPriceTolerance, PaymentAmount = paymentAmount, @@ -78,11 +100,10 @@ public static PricingMode Classic(ulong paymentAmount, byte gasPriceTolerance = /// The cost of the transaction is determined by the cost table, per the transaction category. /// /// Defaults to 1. Gas price tolerance admitted. - public static PricingMode Fixed(byte gasPriceTolerance = 1) + public static IPricingMode Fixed(byte gasPriceTolerance = 1) { - return new PricingMode() + return new FixedPricingMode() { - Type = PricingModeType.Fixed, GasPriceTolerance = gasPriceTolerance, }; } @@ -91,22 +112,23 @@ public static PricingMode Fixed(byte gasPriceTolerance = 1) /// The payment for this transaction was previously reserved, as proven by the receipt hash. /// /// Pre-paid receipt. - public static PricingMode Reserved(string receipt) + public static IPricingMode Reserved(string receipt) { - return new PricingMode() + return new ReservedPricingMode() { - Type = PricingModeType.Reserved, Receipt = receipt, }; } - public class PricingModeConverter : JsonConverter + public class PricingModeConverter : JsonConverter { - public override PricingMode Read( + public override IPricingMode Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + IPricingMode pricingMode = null; + if (reader.TokenType != JsonTokenType.StartObject) throw new JsonException("Cannot deserialize PricingMode. StartObject expected"); reader.Read(); @@ -114,86 +136,56 @@ public override PricingMode Read( if (reader.TokenType != JsonTokenType.PropertyName) throw new JsonException("Cannot deserialize PricingMode. PropertyName expected"); - string pricingModeType = reader.GetString(); + if(!EnumCompat.TryParse(reader.GetString(), out PricingModeType pricingModeType)) + throw new Exception($"Unknown pricing mode '{reader.GetString()}'"); + reader.Read(); if (reader.TokenType != JsonTokenType.StartObject) throw new JsonException("Cannot deserialize PricingMode. StartObject expected"); - reader.Read(); - - ulong paymentAmount = 0; - byte gasPriceTolerance = 0; - bool standardPayment = false; - string receipt = null; - - while (reader.TokenType == JsonTokenType.PropertyName) + + switch (pricingModeType) { - var field = reader.GetString(); - reader.Read(); - switch (field) - { - case "payment_amount": - paymentAmount = reader.GetUInt64(); - break; - case "standard_payment": - standardPayment = reader.GetBoolean(); - break; - case "gas_price_tolerance": - gasPriceTolerance = reader.GetByte(); - break; - case "receipt": - receipt = reader.GetString(); - break; - } - - reader.Read(); + case PricingModeType.Classic: + pricingMode = JsonSerializer.Deserialize(ref reader, options); + break; + case PricingModeType.Fixed: + pricingMode = JsonSerializer.Deserialize(ref reader, options); + break; + case PricingModeType.Reserved: + pricingMode = JsonSerializer.Deserialize(ref reader, options); + break; } - reader.Read(); - - return new PricingMode() - { - Type = EnumCompat.Parse(pricingModeType), - PaymentAmount = paymentAmount, - GasPriceTolerance = gasPriceTolerance, - StandardPayment = standardPayment, - Receipt = receipt, - }; + + return pricingMode; } public override void Write( Utf8JsonWriter writer, - PricingMode value, + IPricingMode value, JsonSerializerOptions options) { - if (value.Type == PricingModeType.Classic) - { - writer.WriteStartObject(); - writer.WritePropertyName("Classic"); - writer.WriteStartObject(); - writer.WriteNumber("payment_amount", value.PaymentAmount); - writer.WriteNumber("gas_price_tolerance", value.GasPriceTolerance); - writer.WriteBoolean("standard_payment", value.StandardPayment); - writer.WriteEndObject(); - writer.WriteEndObject(); - } - else if (value.Type == PricingModeType.Fixed) - { - writer.WriteStartObject(); - writer.WritePropertyName("Fixed"); - writer.WriteStartObject(); - writer.WriteNumber("gas_price_tolerance", value.GasPriceTolerance); - writer.WriteEndObject(); - writer.WriteEndObject(); - } - else if (value.Type == PricingModeType.Reserved) + switch (value) { - writer.WriteStartObject(); - writer.WritePropertyName("Reserved"); - writer.WriteStartObject(); - writer.WriteString("receipt", value.Receipt); - writer.WriteEndObject(); - writer.WriteEndObject(); + case ClassicPricingMode classicPricingMode: + writer.WriteStartObject(); + writer.WritePropertyName("Classic"); + JsonSerializer.Serialize(writer, classicPricingMode); + writer.WriteEndObject(); + break; + case FixedPricingMode fixedPricingMode: + writer.WriteStartObject(); + writer.WritePropertyName("Fixed"); + JsonSerializer.Serialize(writer, fixedPricingMode); + writer.WriteEndObject(); + break; + case ReservedPricingMode reservedPricingMode: + writer.WriteStartObject(); + writer.WritePropertyName("Reserved"); + JsonSerializer.Serialize(writer, reservedPricingMode); + writer.WriteEndObject(); + break; } } } diff --git a/Casper.Network.SDK/Types/Transaction.cs b/Casper.Network.SDK/Types/Transaction.cs index 533aa13..657558e 100644 --- a/Casper.Network.SDK/Types/Transaction.cs +++ b/Casper.Network.SDK/Types/Transaction.cs @@ -72,7 +72,7 @@ public TransactionVersion Version /// /// Pricing mode of a Transaction. /// - public PricingMode PricingMode { get; set; } + public IPricingMode PricingMode { get; set; } public ITransactionScheduling Scheduling { get; set; } @@ -236,27 +236,17 @@ public static explicit operator Transaction(Deploy deploy) throw new ArgumentException("No valid session object in the deploy to convert to Transaction"); } - PricingMode pricingMode; + IPricingMode pricingMode; if (deploy.Payment is ModuleBytesDeployItem paymentModule) { var amountArg = paymentModule.RuntimeArgs.FirstOrDefault(arg => arg.Name == "amount"); if (amountArg == null) - pricingMode = new PricingMode() - { - Type = PricingModeType.Classic, - StandardPayment = false, - }; + pricingMode = Types.PricingMode.Classic(0, 1, false); else { - var paymentAmount = amountArg.Value.ToBigInteger(); - pricingMode = new PricingMode() - { - Type = PricingModeType.Classic, - StandardPayment = paymentModule.ModuleBytes == null, - PaymentAmount = (ulong)paymentAmount, - }; + var paymentAmount = (ulong)amountArg.Value.ToBigInteger(); + pricingMode = Types.PricingMode.Classic(paymentAmount, 1, paymentModule.ModuleBytes == null); } - } else { diff --git a/Casper.Network.SDK/Types/TransactionV1Header.cs b/Casper.Network.SDK/Types/TransactionV1Header.cs index d1418ff..3ef32f5 100644 --- a/Casper.Network.SDK/Types/TransactionV1Header.cs +++ b/Casper.Network.SDK/Types/TransactionV1Header.cs @@ -36,7 +36,7 @@ public class TransactionV1Header /// [JsonPropertyName("pricing_mode")] [JsonConverter(typeof(PricingMode.PricingModeConverter))] - public PricingMode PricingMode { get; set; } + public IPricingMode PricingMode { get; set; } /// /// Hash of the body part of this Deploy. From 3695df49e375d4fed06d89905cff772e81faac50 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 24 Jul 2024 13:00:45 +0200 Subject: [PATCH 076/126] Added TransactionBuilder class Signed-off-by: David Hernando --- Casper.Network.SDK/Types/Transaction.cs | 6 +- .../Types/TransactionBuilder.cs | 340 ++++++++++++++++++ .../Types/TransactionV1EntryPoint.cs | 6 +- 3 files changed, 348 insertions(+), 4 deletions(-) create mode 100644 Casper.Network.SDK/Types/TransactionBuilder.cs diff --git a/Casper.Network.SDK/Types/Transaction.cs b/Casper.Network.SDK/Types/Transaction.cs index 657558e..8ebbc7e 100644 --- a/Casper.Network.SDK/Types/Transaction.cs +++ b/Casper.Network.SDK/Types/Transaction.cs @@ -26,7 +26,7 @@ internal class TransactionCompat /// /// A versioned wrapper for a TransactionV1 or Deploy. /// - public class Transaction + public partial class Transaction { protected TransactionVersion _version; @@ -241,11 +241,11 @@ public static explicit operator Transaction(Deploy deploy) { var amountArg = paymentModule.RuntimeArgs.FirstOrDefault(arg => arg.Name == "amount"); if (amountArg == null) - pricingMode = Types.PricingMode.Classic(0, 1, false); + pricingMode = Types.PricingMode.Classic(0, (byte)deploy.Header.GasPrice, false); else { var paymentAmount = (ulong)amountArg.Value.ToBigInteger(); - pricingMode = Types.PricingMode.Classic(paymentAmount, 1, paymentModule.ModuleBytes == null); + pricingMode = Types.PricingMode.Classic(paymentAmount, (byte)deploy.Header.GasPrice, paymentModule.ModuleBytes == null); } } else diff --git a/Casper.Network.SDK/Types/TransactionBuilder.cs b/Casper.Network.SDK/Types/TransactionBuilder.cs new file mode 100644 index 0000000..9d8f7ba --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionBuilder.cs @@ -0,0 +1,340 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using Casper.Network.SDK.Utils; + +namespace Casper.Network.SDK.Types +{ + public partial class Transaction + { + public abstract class TransactionV1Builder where T : TransactionV1Builder + { + protected InitiatorAddr _from = null; + protected string _chainName = null; + protected DateTime? _timestamp = null; + protected ulong _ttl = 1800000; //30m + protected byte _gasPriceTolerance = 1; + + protected ITransactionV1Target _invocationTarget; + protected ITransactionV1EntryPoint _entryPoint; + protected TransactionCategory _category; + protected ITransactionV1Scheduling _scheduling = TransactionScheduling.Standard; + protected List _runtimeArgs = new(); + + public T From(PublicKey publicKey) + { + _from = Types.InitiatorAddr.FromPublicKey(publicKey); + return (T)this; + } + + public T From(AccountHashKey accountHashKey) + { + _from = Types.InitiatorAddr.FromAccountHash(accountHashKey); + return (T)this; + } + + public T ChainName(string chainName) + { + _chainName = chainName; + return (T)this; + } + + public T Timestamp(DateTime timestamp) + { + _timestamp = timestamp; + return (T)this; + } + + public T TTL(ulong ttl) + { + _ttl = ttl; + return (T)this; + } + + public T GasPriceTolerance(byte gasPriceTolerance) + { + _gasPriceTolerance = gasPriceTolerance; + return (T)this; + } + + public virtual TransactionV1 Build() + { + var body = new TransactionV1Body() + { + RuntimeArgs = _runtimeArgs, + Target = _invocationTarget, + EntryPoint = _entryPoint, + Scheduling = _scheduling, + Category = _category, + }; + var header = new TransactionV1Header() + { + InitiatorAddr = _from, + Timestamp = DateUtils.ToEpochTime(_timestamp.HasValue ? _timestamp.Value : DateTime.UtcNow), + Ttl = _ttl, + ChainName = _chainName, + PricingMode = Types.PricingMode.Fixed(_gasPriceTolerance), + }; + var transaction = new TransactionV1(header, body); + return transaction; + } + } + + public class NativeTransferBuilder : TransactionV1Builder + { + //specific tx properties + private CLValue _target = null; + private CLValue _amount = CLValue.U512((BigInteger)0); + private ulong? _idTransfer = null; + + public NativeTransferBuilder() + { + _invocationTarget = TransactionV1Target.Native; + _entryPoint = TransactionV1EntryPoint.Transfer; + _category = TransactionCategory.Mint; + } + + public NativeTransferBuilder Target(PublicKey publicKey) + { + _target = CLValue.PublicKey(publicKey); + return this; + } + + public NativeTransferBuilder Target(AccountHashKey accountHashKey) + { + _target = CLValue.ByteArray(accountHashKey.RawBytes); + return this; + } + + public NativeTransferBuilder Amount(ulong amount) + { + _amount = CLValue.U512(amount); + return this; + } + + public NativeTransferBuilder Amount(BigInteger amount) + { + _amount = CLValue.U512(amount); + return this; + } + + public NativeTransferBuilder Id(ulong id) + { + _idTransfer = id; + return this; + } + + public override TransactionV1 Build() + { + _runtimeArgs = new List(); + _runtimeArgs.Add(new NamedArg("target", _target)); + _runtimeArgs.Add(new NamedArg("amount", _amount)); + if (_idTransfer.HasValue) + { + _runtimeArgs.Add(new NamedArg("id", CLValue.Option(CLValue.U64(_idTransfer.Value)))); + } + + return base.Build(); + } + } + + public class NativeDelegateBuilder : TransactionV1Builder + { + //specific tx properties + private CLValue _validator = null; + private CLValue _amount = CLValue.U512((BigInteger)0); + + public NativeDelegateBuilder() + { + _invocationTarget = TransactionV1Target.Native; + _entryPoint = TransactionV1EntryPoint.Delegate; + _category = TransactionCategory.Auction; + } + + public NativeDelegateBuilder Validator(PublicKey publicKey) + { + _validator = CLValue.PublicKey(publicKey); + return this; + } + + public NativeDelegateBuilder Amount(ulong amount) + { + _amount = CLValue.U512(amount); + return this; + } + + public NativeDelegateBuilder Amount(BigInteger amount) + { + _amount = CLValue.U512(amount); + return this; + } + + public override TransactionV1 Build() + { + _runtimeArgs = new List(); + _runtimeArgs.Add(new NamedArg("delegator", CLValue.PublicKey(_from.PublicKey))); + _runtimeArgs.Add(new NamedArg("validator", _validator)); + _runtimeArgs.Add(new NamedArg("amount", _amount)); + + return base.Build(); + } + } + + public class NativeUndelegateBuilder : TransactionV1Builder + { + //specific tx properties + private CLValue _validator = null; + private CLValue _amount = CLValue.U512((BigInteger)0); + + public NativeUndelegateBuilder() + { + _invocationTarget = TransactionV1Target.Native; + _entryPoint = TransactionV1EntryPoint.Undelegate; + _category = TransactionCategory.Auction; + } + + public NativeUndelegateBuilder Validator(PublicKey publicKey) + { + _validator = CLValue.PublicKey(publicKey); + return this; + } + + public NativeUndelegateBuilder Amount(ulong amount) + { + _amount = CLValue.U512(amount); + return this; + } + + public NativeUndelegateBuilder Amount(BigInteger amount) + { + _amount = CLValue.U512(amount); + return this; + } + + public override TransactionV1 Build() + { + _runtimeArgs = new List(); + _runtimeArgs.Add(new NamedArg("delegator", CLValue.PublicKey(_from.PublicKey))); + _runtimeArgs.Add(new NamedArg("validator", _validator)); + _runtimeArgs.Add(new NamedArg("amount", _amount)); + + return base.Build(); + } + } + + public class ContractCallBuilder : TransactionV1Builder + { + public ContractCallBuilder() + { + _category = TransactionCategory.Small; + } + + public ContractCallBuilder ByHash(string contractHash) + { + _invocationTarget = TransactionV1Target.StoredByHash(contractHash); + return this; + } + + public ContractCallBuilder ByName(string name) + { + _invocationTarget = TransactionV1Target.StoredByName(name); + return this; + } + + public ContractCallBuilder ByPackageHash(string contractHash) + { + _invocationTarget = TransactionV1Target.StoredByPackageHash(contractHash); + return this; + } + + public ContractCallBuilder ByPackageName(string name) + { + _invocationTarget = TransactionV1Target.StoredByPackageName(name); + return this; + } + + public ContractCallBuilder EntryPoint(string name) + { + _entryPoint = TransactionV1EntryPoint.Custom(name); + return this; + } + + public ContractCallBuilder InstallCategory() + { + _category = TransactionCategory.Large; + return this; + } + + public ContractCallBuilder LargeCategory() + { + _category = TransactionCategory.Large; + return this; + } + + public ContractCallBuilder MediumCategory() + { + _category = TransactionCategory.Medium; + return this; + } + + public ContractCallBuilder SmallCategory() + { + _category = TransactionCategory.Small; + return this; + } + + public ContractCallBuilder RuntimeArgs(List args) + { + _runtimeArgs = args; + return this; + } + } + + public class SessionBuilder : TransactionV1Builder + { + private byte[] _wasm = null; + + public SessionBuilder() + { + _category = TransactionCategory.InstallUpgrade; + _entryPoint = TransactionV1EntryPoint.Call; + } + + public SessionBuilder Wasm(byte[] wasmBytes) + { + _invocationTarget = TransactionV1Target.Session(wasmBytes); + return this; + } + + public SessionBuilder InstallCategory() + { + _category = TransactionCategory.Large; + return this; + } + + public SessionBuilder LargeCategory() + { + _category = TransactionCategory.Large; + return this; + } + + public SessionBuilder MediumCategory() + { + _category = TransactionCategory.Medium; + return this; + } + + public SessionBuilder SmallCategory() + { + _category = TransactionCategory.Small; + return this; + } + + public SessionBuilder RuntimeArgs(List args) + { + _runtimeArgs = args; + return this; + } + } + } +} diff --git a/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs b/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs index b3aceb3..176842f 100644 --- a/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs +++ b/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs @@ -105,7 +105,8 @@ public CustomTransactionV1EntryPoint(string name) /// public class TransactionV1EntryPoint { - public static ITransactionV1EntryPoint Transfer => new NativeTransactionV1EntryPoint(NativeEntryPoint.Transfer); + public static ITransactionV1EntryPoint Transfer => + new NativeTransactionV1EntryPoint(NativeEntryPoint.Transfer); public static ITransactionV1EntryPoint AddBid => new NativeTransactionV1EntryPoint(NativeEntryPoint.AddBid); @@ -131,6 +132,9 @@ public class TransactionV1EntryPoint public static ITransactionV1EntryPoint Call => new NativeTransactionV1EntryPoint(NativeEntryPoint.Call); + public static ITransactionV1EntryPoint Custom(string name) => + new CustomTransactionV1EntryPoint(name); + public class TransactionEntryPointConverter : JsonConverter { public override ITransactionV1EntryPoint Read( From 1764abc6df2cb9d3360ad0d02f27a658725671f2 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 2 Aug 2024 10:07:33 +0200 Subject: [PATCH 077/126] Bugfix: for a v156 deploy, list of transforms was not correctly converted to the new Transform abstraction. Signed-off-by: David Hernando --- .../TestData/deploy-testing-effects-v156.json | 235 ++++++++++++++ .../get-transaction-session-v200.json | 2 +- Casper.Network.SDK.Test/TransformsTest.cs | 61 ++++ Casper.Network.SDK/Types/Transform.cs | 287 +++++++++++------- Casper.Network.SDK/Types/TransformV1.cs | 2 + 5 files changed, 477 insertions(+), 110 deletions(-) create mode 100644 Casper.Network.SDK.Test/TestData/deploy-testing-effects-v156.json create mode 100644 Casper.Network.SDK.Test/TransformsTest.cs diff --git a/Casper.Network.SDK.Test/TestData/deploy-testing-effects-v156.json b/Casper.Network.SDK.Test/TestData/deploy-testing-effects-v156.json new file mode 100644 index 0000000..4dc2e8f --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/deploy-testing-effects-v156.json @@ -0,0 +1,235 @@ +{ + "api_version": "1.5.7", + "deploy": { + "hash": "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", + "header": { + "account": "0203000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", + "timestamp": "2020-01-01T00:00:00.000Z", + "ttl": "30m", + "gas_price": 1, + "body_hash": "0101010101010101010101010101010101010101010101010101010101010101", + "dependencies": [], + "chain_name": "casper-test" + }, + "payment": { + "ModuleBytes": { + "module_bytes": "", + "args": [ + [ + "amount", + { + "cl_type": "U512", + "bytes": "050010a5d4e8", + "parsed": "1000000000000" + } + ] + ] + } + }, + "session": { + "ModuleBytes": { + "module_bytes": "0001020304", + "args": [ + [ + "decimals", + { + "cl_type": "U8", + "bytes": "12", + "parsed": 18 + } + ] + ] + } + }, + "approvals": [ + { + "signer": "0203000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", + "signature": "02000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f" + } + ] + }, + "execution_results": [ + { + "block_hash": "0202020202020202020202020202020202020202020202020202020202020202", + "result": { + "Success": { + "effect": { + "operations": [], + "transforms": [ + { + "key": "account-hash-0303030303030303030303030303030303030303030303030303030303030303", + "transform": "Identity" + }, + { + "key": "hash-0303030303030303030303030303030303030303030303030303030303030303", + "transform": "Identity" + }, + { + "key": "balance-0303030303030303030303030303030303030303030303030303030303030303", + "transform": { + "WriteCLValue": { + "cl_type": "U512", + "bytes": "050010a5d4e8", + "parsed": "1000000000000" + } + } + }, + { + "key": "balance-0909090909090909090909090909090909090909090909090909090909090909", + "transform": { + "AddUInt64": 555 + } + }, + { + "key": "balance-0303030303030303030303030303030303030303030303030303030303030303", + "transform": { + "AddUInt512": "1000000000000" + } + }, + { + "key": "uref-0303030303030303030303030303030303030303030303030303030303030303-000", + "transform": { + "WriteCLValue": { + "cl_type": "String", + "bytes": "030000004a4a4a", + "parsed": "JJJ" + } + } + }, + { + "key": "uref-0303030303030303030303030303030303030303030303030303030303030303-000", + "transform": { + "WriteCLValue": { + "cl_type": "U8", + "bytes": "12", + "parsed": 18 + } + } + }, + { + "key": "uref-0303030303030303030303030303030303030303030303030303030303030303-000", + "transform": { + "WriteCLValue": { + "cl_type": "Unit", + "bytes": "", + "parsed": null + } + } + }, + { + "key": "hash-0505050505050505050505050505050505050505050505050505050505050505", + "transform": "WriteContractPackage" + }, + { + "key": "account-hash-0505050505050505050505050505050505050505050505050505050505050505", + "transform": { + "AddKeys": [ + { + "name": "cep18_contract_package_JJJ", + "key": "hash-0505050505050505050505050505050505050505050505050505050505050505" + } + ] + } + }, + { + "key": "hash-0707070707070707070707070707070707070707070707070707070707070707", + "transform": "WriteContractWasm" + }, + { + "key": "hash-0808080808080808080808080808080808080808080808080808080808080808", + "transform": "WriteContract" + }, + { + "key": "hash-0909090909090909090909090909090909090909090909090909090909090909", + "transform": "WriteContractPackage" + }, + { + "key": "transfer-0101010101010101010101010101010101010101010101010101010101010101", + "transform": { + "WriteTransfer": { + "deploy_hash": "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", + "from": "account-hash-0101010101010101010101010101010101010101010101010101010101010101", + "to": "account-hash-0202020202020202020202020202020202020202020202020202020202020202", + "source": "uref-0101010101010101010101010101010101010101010101010101010101010101-007", + "target": "uref-0202020202020202020202020202020202020202020202020202020202020202-004", + "amount": "777777777", + "gas": "0", + "id": 9 + } + } + }, + { + "key": "deploy-000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", + "transform": { + "WriteDeployInfo": { + "deploy_hash": "000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", + "transfers": [ + "transfer-0101010101010101010101010101010101010101010101010101010101010101" + ], + "from": "account-hash-0101010101010101010101010101010101010101010101010101010101010101", + "source": "uref-0101010101010101010101010101010101010101010101010101010101010101-007", + "gas": "555555555555" + } + } + }, + { + "key": "bid-0101010101010101010101010101010101010101010101010101010101010101", + "transform": { + "WriteBid": { + "validator_public_key": "010808080808080808080808080808080808080808080808080808080808080808", + "bonding_purse": "uref-0808080808080808080808080808080808080808080808080808080808080808-007", + "staked_amount": "1000000000000", + "delegation_rate": 10, + "vesting_schedule": { + "initial_release_timestamp_millis": 1722581889000, + "locked_amounts": [ + "1300000000000000", + "1200000000000000", + "0" + ] + }, + "delegators": { + + "02030909090909090909090909090909090909090909090909090909090909090909": { + "delegator_public_key": "02030909090909090909090909090909090909090909090909090909090909090909", + "staked_amount": "4000000000000", + "bonding_purse": "uref-0909090909090909090909090909090909090909090909090909090909090909-007", + "validator_public_key": "010808080808080808080808080808080808080808080808080808080808080808", + "vesting_schedule": null + }, + "02030a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a": { + "delegator_public_key": "02030a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a", + "staked_amount": "5000000000000", + "bonding_purse": "uref-0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a-007", + "validator_public_key": "010808080808080808080808080808080808080808080808080808080808080808", + "vesting_schedule": null + } + }, + "inactive": false + } + } + }, + { + "key": "unbond-0101010101010101010101010101010101010101010101010101010101010101", + "transform": { + "WriteUnbonding": [ + { + "bonding_purse": "uref-0808080808080808080808080808080808080808080808080808080808080808-007", + "validator_public_key": "010808080808080808080808080808080808080808080808080808080808080808", + "unbonder_public_key": "010808080808080808080808080808080808080808080808080808080808080808", + "era_of_creation": 10, + "amount": "600600600600600", + "new_validator": null + } + ] + } + } + ] + }, + "transfers": [], + "cost": "600600600" + } + } + } + ] +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json b/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json index 878f5e8..e8e323b 100644 --- a/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json +++ b/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json @@ -10,7 +10,7 @@ "body_hash": "9b1d4002bb1af17ec9fc4193ac5ba7db6535b6dd813034b730829a94b13ebc46", "pricing_mode": { "Fixed": { - "gas_price_tolerance": 3 + "gas_price_tolerance": 2 } }, "initiator_addr": { diff --git a/Casper.Network.SDK.Test/TransformsTest.cs b/Casper.Network.SDK.Test/TransformsTest.cs new file mode 100644 index 0000000..75aad7b --- /dev/null +++ b/Casper.Network.SDK.Test/TransformsTest.cs @@ -0,0 +1,61 @@ +using System.IO; +using System.Linq; +using System.Numerics; +using Casper.Network.SDK.JsonRpc.ResultTypes; +using Casper.Network.SDK.Types; +using NUnit.Framework; + +namespace NetCasperTest +{ + public class TransformsTest + { + [Test] + public void TransformsTest_v156() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/deploy-testing-effects-v156.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + + var effects = result.ExecutionInfo.ExecutionResult.Effect; + Assert.AreEqual(2, effects.Count(t => t.Kind is IdentityTransformKind)); + + Assert.AreEqual(4, effects.Count(t => t.Kind is WriteTransformKind kind && + kind.Value.CLValue is not null)); + Assert.AreEqual(1, effects.Count(t => t.Kind is WriteTransformKind kind && + kind.Value.CLValue is not null && + kind.Value.CLValue.TypeInfo.Type == CLType.String && + kind.Value.CLValue.ToString().Equals("JJJ"))); + + var addU64Kind = + effects.FirstOrDefault(t => t.Kind is AddUInt64TransformKind).Kind as AddUInt64TransformKind; + Assert.AreEqual(555, addU64Kind.Value); + var addU512Kind = + effects.FirstOrDefault(t => t.Kind is AddUInt512TransformKind).Kind as AddUInt512TransformKind; + Assert.AreEqual(new BigInteger(1000000000000), addU512Kind.Value); + + var addKeysKind = + effects.FirstOrDefault(t => t.Kind is AddKeysTransformKind).Kind as AddKeysTransformKind; + Assert.AreEqual(1, addKeysKind.Value.Count); + Assert.AreEqual("cep18_contract_package_JJJ", addKeysKind.Value[0].Name); + Assert.AreEqual("hash-0505050505050505050505050505050505050505050505050505050505050505", addKeysKind.Value[0].Key.ToString()); + + Assert.AreEqual(2, effects.Count(t => t.Kind is WriteContractPackageLegacyTransformKind)); + Assert.AreEqual(1, effects.Count(t => t.Kind is WriteContractPackageLegacyTransformKind && + t.Key.ToString().Equals("hash-0505050505050505050505050505050505050505050505050505050505050505"))); + Assert.AreEqual(1, effects.Count(t => t.Kind is WriteContractLegacyTransformKind)); + Assert.AreEqual(1, effects.Count(t => t.Kind is WriteContractWasmLegacyTransformKind)); + + Assert.AreEqual(1, effects.Count(t => t.Kind is WriteTransformKind kind && + kind.Value.DeployInfo is not null)); + Assert.AreEqual(1, effects.Count(t => t.Kind is WriteTransformKind kind && + kind.Value.Transfer is not null)); + Assert.AreEqual(1, effects.Count(t => t.Kind is WriteTransformKind kind && + kind.Value.Bid is not null)); + Assert.AreEqual(1, effects.Count(t => t.Kind is WriteTransformKind kind && + kind.Value.Unbonding is not null)); + } + + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Transform.cs b/Casper.Network.SDK/Types/Transform.cs index 48b26fd..39bd87b 100644 --- a/Casper.Network.SDK/Types/Transform.cs +++ b/Casper.Network.SDK/Types/Transform.cs @@ -7,70 +7,127 @@ namespace Casper.Network.SDK.Types { - public enum TransformKind + public enum TransformKindV2 { - /// - /// An identity transformation that does not modify a value in the global state. Created as a result of - /// reading from the global state. - /// Identity, - - /// - /// Writes a new value (StoredValue) in the global state. - /// Write, - - /// - /// A wrapping addition of an `i32` to an existing numeric value (not necessarily an `i32`) in the global state. - /// AddInt32, - - /// - /// A wrapping addition of a `u64` to an existing numeric value (not necessarily an `u64`) in the global state. - /// AddUInt64, - - /// - /// A wrapping addition of a `U128` to an existing numeric value (not necessarily an `U128`) in the global state. - /// AddUInt128, - - /// - /// A wrapping addition of a `U256` to an existing numeric value (not necessarily an `U256`) in the global state. - /// AddUInt256, - - /// - /// A wrapping addition of a `U512` to an existing numeric value (not necessarily an `U512`) in the global state. - /// AddUInt512, - - /// - /// Adds new named keys to an existing entry in the global state.\n\nThis transform assumes that the existing stored - /// value is either an Account or a Contract. - /// AddKeys, - - /// - /// Removes the pathing to the global state entry of the specified key. The pruned element remains reachable from - /// previously generated global state root hashes, but will not be included in the next generated global state - /// root hash and subsequent state accumulated from it. - /// Prune, - - /// - /// Represents the case where applying a transform would cause an error. - /// Failure, } + public abstract class TransformKind + { + public object Value { get; init; } + } + + public abstract class TransformKind : TransformKind + { + public new abstract T Value { get; init; } + } + /// - /// Representation of a single transformation occurring during execution.\n\nNote that all arithmetic - /// variants of `TransformKindV2` are commutative which means that a given collection of them can be - /// executed in any order to produce the same end result. + /// An identity transformation that does not modify a value in the global state. Created as a result of + /// reading from the global state. /// - public class Kind + public class IdentityTransformKind : TransformKind { + public override object Value { get; init; } + } + + /// + /// Writes a new value (StoredValue) in the global state. + /// + public class WriteTransformKind : TransformKind + { + public override StoredValue Value { get; init; } + } + + /// + /// A wrapping addition of an `i32` to an existing numeric value (not necessarily an `i32`) in the global state. + /// + public class AddInt32TransformKind : TransformKind + { + public override int Value { get; init; } + } + + /// + /// A wrapping addition of a `u64` to an existing numeric value (not necessarily an `u64`) in the global state. + /// + public class AddUInt64TransformKind : TransformKind + { + public override ulong Value { get; init; } + } + + /// + /// A wrapping addition of a `U128` to an existing numeric value (not necessarily an `U128`) in the global state. + /// + public class AddUInt128TransformKind : TransformKind + { + public override BigInteger Value { get; init; } + } + + /// + /// A wrapping addition of a `U256` to an existing numeric value (not necessarily an `U256`) in the global state. + /// + public class AddUInt256TransformKind : TransformKind + { + public override BigInteger Value { get; init; } + } + + /// + /// A wrapping addition of a `U512` to an existing numeric value (not necessarily an `U512`) in the global state. + /// + public class AddUInt512TransformKind : TransformKind + { + public override BigInteger Value { get; init; } + } + + /// + /// Adds new named keys to an existing entry in the global state.\n\nThis transform assumes that the existing stored + /// value is either an Account or a Contract. + /// + public class AddKeysTransformKind : TransformKind> + { + public override List Value { get; init; } + } + + /// + /// Removes the pathing to the global state entry of the specified key. The pruned element remains reachable from + /// previously generated global state root hashes, but will not be included in the next generated global state + /// root hash and subsequent state accumulated from it. + /// + public class PruneTransformKind : TransformKind + { + public override GlobalStateKey Value { get; init; } + } + + /// + /// Represents the case where applying a transform would cause an error. + /// + public class FailureTransformKind : TransformKind + { + public override string Value { get; init; } + } + + public class WriteContractLegacyTransformKind : TransformKind + { + public override object Value { get; init; } + } + + public class WriteContractPackageLegacyTransformKind : TransformKind + { + public override object Value { get; init; } + } + + public class WriteContractWasmLegacyTransformKind : TransformKind + { + public override object Value { get; init; } } /// @@ -87,43 +144,54 @@ public int Version { get { return _version; } } - + protected TransformV1 _transformV1; public static explicit operator TransformV1(Transform transform) { - if(transform._version == 1) + if (transform._version == 1) return transform._transformV1; throw new InvalidCastException("Version2 transform cannot be converted to Version1"); } - + public static explicit operator Transform(TransformV1 transform) { TransformKind kind = transform.Kind switch { - TransformKindV1.Identity => TransformKind.Identity, - TransformKindV1.WriteAccount => TransformKind.Write, - TransformKindV1.WriteAddressableEntity => TransformKind.Write, - TransformKindV1.WriteBid => TransformKind.Write, - TransformKindV1.WriteBidKind => TransformKind.Write, - TransformKindV1.WriteCLValue => TransformKind.Write, - TransformKindV1.WriteContract => TransformKind.Write, - TransformKindV1.WriteContractPackage => TransformKind.Write, - TransformKindV1.WriteContractWasm => TransformKind.Write, - TransformKindV1.WriteDeployInfo => TransformKind.Write, - TransformKindV1.WriteEraInfo => TransformKind.Write, - TransformKindV1.WriteTransfer => TransformKind.Write, - TransformKindV1.WriteUnbonding => TransformKind.Write, - TransformKindV1.WriteWithdraw => TransformKind.Write, - TransformKindV1.AddInt32 => TransformKind.AddInt32, - TransformKindV1.AddUInt64 => TransformKind.AddUInt64, - TransformKindV1.AddUInt128 => TransformKind.AddUInt128, - TransformKindV1.AddUInt256 => TransformKind.AddUInt256, - TransformKindV1.AddUInt512 => TransformKind.AddUInt512, - TransformKindV1.AddKeys => TransformKind.AddKeys, - TransformKindV1.Failure => TransformKind.Failure, - TransformKindV1.Prune => TransformKind.Prune, + TransformKindV1.Identity => new IdentityTransformKind(), + TransformKindV1.WriteAccount => new WriteTransformKind() + { Value = new StoredValue() { Account = transform.Value as Account } }, + // TransformKindV1.WriteAddressableEntity => new WriteTransformKind() + // { Value = new StoredValue() { AddressableEntity = transform.Value as AddressableEntity } }, + TransformKindV1.WriteBid => new WriteTransformKind() + { Value = new StoredValue() { Bid = transform.Value as Bid } }, + // TransformKindV1.WriteBidKind => new WriteTransformKind() + // { Value = new StoredValue() { BidKind = transform.Value as BidKind } }, + TransformKindV1.WriteCLValue => new WriteTransformKind() + { Value = new StoredValue() { CLValue = transform.Value as CLValue } }, + TransformKindV1.WriteContract => new WriteContractLegacyTransformKind(), + TransformKindV1.WriteContractPackage => new WriteContractPackageLegacyTransformKind(), + TransformKindV1.WriteContractWasm => new WriteContractWasmLegacyTransformKind(), + TransformKindV1.WriteDeployInfo => new WriteTransformKind() + { Value = new StoredValue() { DeployInfo = transform.Value as DeployInfo } }, + TransformKindV1.WriteEraInfo => new WriteTransformKind() + { Value = new StoredValue() { EraInfo = transform.Value as EraInfo } }, + TransformKindV1.WriteTransfer => new WriteTransformKind() + { Value = new StoredValue() { Transfer = (Transfer)(transform.Value as TransferV1) } }, + TransformKindV1.WriteUnbonding => new WriteTransformKind() + { Value = new StoredValue() { Unbonding = transform.Value as List } }, + TransformKindV1.WriteWithdraw => new WriteTransformKind() + { Value = new StoredValue() { Withdraw = transform.Value as List } }, + TransformKindV1.AddInt32 => new AddInt32TransformKind() { Value = (int)transform.Value }, + TransformKindV1.AddUInt64 => new AddUInt64TransformKind() { Value = (ulong)transform.Value }, + TransformKindV1.AddUInt128 => new AddUInt128TransformKind() { Value = (BigInteger)transform.Value }, + TransformKindV1.AddUInt256 => new AddUInt256TransformKind() { Value = (BigInteger)transform.Value }, + TransformKindV1.AddUInt512 => new AddUInt512TransformKind() { Value = (BigInteger)transform.Value }, + TransformKindV1.AddKeys => new AddKeysTransformKind() { Value = transform.Value as List }, + TransformKindV1.Failure => new FailureTransformKind() { Value = (string)transform.Value }, + // TransformKindV1.Prune => new PruneTransformKind() {Value = transform.Value as GlobalStateKey }, + _ => throw new Exception("Cannot convert '" + transform.Kind + "' to TransofrmKind v2."), }; return new Transform @@ -131,11 +199,10 @@ public static explicit operator Transform(TransformV1 transform) _version = 1, _transformV1 = transform, Key = transform.Key, - TransformKind = kind, - Value = transform.Value, + Kind = kind, }; } - + /// /// The formatted string of the `Key`. /// @@ -147,12 +214,7 @@ public static explicit operator Transform(TransformV1 transform) /// variants of `TransformKind` are commutative which means that a given collection of them can be /// executed in any order to produce the same end result. /// - public TransformKind TransformKind { get; init; } - - /// - /// Data associated to some type of transforms - /// - public object Value { get; init; } + public TransformKind Kind { get; init; } public class TransformConverter : JsonConverter { @@ -183,54 +245,62 @@ public override Transform Read( { if (reader.TokenType == JsonTokenType.String) { - var stype = reader.GetString(); - if (stype != null) - kind = EnumCompat.Parse(stype); + if (TransformKindV2.Identity.ToString().Equals(reader.GetString())) + kind = new IdentityTransformKind(); + else + throw new Exception("Unexpected transform kind: " + reader.GetString()); reader.Read(); } else if (reader.TokenType == JsonTokenType.StartObject) { - reader.Read(); + reader.Read(); // skip start object var stype = reader.GetString(); - if (stype != null) - kind = EnumCompat.Parse(stype); + var kindv2 = EnumCompat.Parse(stype); reader.Read(); - switch (kind) + switch (kindv2) { - case TransformKind.Write: - value = JsonSerializer.Deserialize(ref reader, options); + case TransformKindV2.Write: + var storedValue = JsonSerializer.Deserialize(ref reader, options); + kind = new WriteTransformKind() { Value = storedValue }; reader.Read(); // end object break; - case TransformKind.AddInt32: - value = reader.GetInt32(); + case TransformKindV2.AddInt32: + kind = new AddInt32TransformKind() { Value = reader.GetInt32() }; reader.Read(); break; - case TransformKind.AddUInt64: - value = reader.GetUInt64(); + case TransformKindV2.AddUInt64: + kind = new AddUInt64TransformKind() { Value = reader.GetUInt64() }; reader.Read(); break; - case TransformKind.AddUInt128: - value = BigInteger.Parse(reader.GetString() ?? "0"); + case TransformKindV2.AddUInt128: + var u128 = BigInteger.Parse(reader.GetString() ?? "0"); + kind = new AddUInt128TransformKind() { Value = u128 }; reader.Read(); break; - case TransformKind.AddUInt256: - value = BigInteger.Parse(reader.GetString() ?? "0"); + case TransformKindV2.AddUInt256: + var u256 = BigInteger.Parse(reader.GetString() ?? "0"); + kind = new AddUInt256TransformKind() { Value = u256 }; reader.Read(); break; - case TransformKind.AddUInt512: - value = BigInteger.Parse(reader.GetString() ?? "0"); + case TransformKindV2.AddUInt512: + var u512 = BigInteger.Parse(reader.GetString() ?? "0"); + kind = new AddUInt512TransformKind() { Value = u512 }; reader.Read(); break; - case TransformKind.AddKeys: - value = JsonSerializer.Deserialize>(ref reader, options); + case TransformKindV2.AddKeys: + var namedKeys = + JsonSerializer.Deserialize>(ref reader, options); + kind = new AddKeysTransformKind() { Value = namedKeys }; reader.Read(); // end array break; - case TransformKind.Prune: - value = GlobalStateKey.FromString(reader.GetString()); + case TransformKindV2.Prune: + var prunedKey = GlobalStateKey.FromString(reader.GetString()); + kind = new PruneTransformKind() { Value = prunedKey }; reader.Read(); break; - case TransformKind.Failure: - value = reader.GetString(); + case TransformKindV2.Failure: + var json = JsonSerializer.Deserialize(ref reader, options); + kind = new FailureTransformKind() { Value = json.GetRawText() }; reader.Read(); break; } @@ -245,8 +315,7 @@ public override Transform Read( return new Transform() { Key = GlobalStateKey.FromString(key), - TransformKind = kind ?? TransformKind.Identity, - Value = value + Kind = kind, }; } diff --git a/Casper.Network.SDK/Types/TransformV1.cs b/Casper.Network.SDK/Types/TransformV1.cs index 7f638b5..b653f0f 100644 --- a/Casper.Network.SDK/Types/TransformV1.cs +++ b/Casper.Network.SDK/Types/TransformV1.cs @@ -167,6 +167,8 @@ public override TransformV1 Read( value = JsonSerializer.Deserialize(ref reader, options); reader.Read(); break; + default: + throw new Exception("Unkown TransformV1 kind: " + type.ToString()); } reader.Read(); //end object From bd277eca042d235012ff85d68ec0869af9e6de3e Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 2 Aug 2024 15:45:53 +0200 Subject: [PATCH 078/126] Added Get*Rewards* methods to ICasperClient Signed-off-by: David Hernando --- Casper.Network.SDK/ICasperClient.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Casper.Network.SDK/ICasperClient.cs b/Casper.Network.SDK/ICasperClient.cs index aaa9713..7d48487 100644 --- a/Casper.Network.SDK/ICasperClient.cs +++ b/Casper.Network.SDK/ICasperClient.cs @@ -123,6 +123,21 @@ Task> GetDictionaryItemByURef(string seedUR Task> GetValidatorChanges(); + Task> GetValidatorReward(PublicKey validator, string blockHash = null); + + Task> GetValidatorReward(PublicKey validator, ulong blockHeight); + + Task> GetValidatorRewardWithEraId(PublicKey validator, ulong eraId); + + Task> GetDelegatorReward(PublicKey validator, PublicKey delegator, + string blockHash = null); + + Task> GetDelegatorReward(PublicKey validator, PublicKey delegator, + ulong blockHeight); + + Task> GetDelegatorRewardWithEraId(PublicKey validator, PublicKey delegator, + ulong eraId); + Task GetRpcSchema(); Task> GetChainspec(); From edb03e77bc7e0063420e23a3d37e00827ba3406e Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 2 Aug 2024 16:01:20 +0200 Subject: [PATCH 079/126] Added switch_block_hash to get_reward response Signed-off-by: David Hernando --- Casper.Network.SDK.Test/RPCResponses/GetRewardResultTest.cs | 3 ++- Casper.Network.SDK.Test/TestData/info-get-reward-v200.json | 2 +- Casper.Network.SDK/JsonRpc/ResultTypes/GetRewardResult.cs | 6 ++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Casper.Network.SDK.Test/RPCResponses/GetRewardResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetRewardResultTest.cs index 6a5cddb..b1f73bb 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetRewardResultTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetRewardResultTest.cs @@ -7,7 +7,7 @@ namespace NetCasperTest.RPCResponses public class GetRewardResultTest { [Test] - public void GetBlockResultTest_v200() + public void GetRewardResultTest_v200() { string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + "/TestData/info-get-reward-v200.json"); @@ -18,6 +18,7 @@ public void GetBlockResultTest_v200() Assert.AreEqual(13, result.EraId); Assert.AreEqual(1, result.DelegationRate); Assert.AreEqual("62559062048560", result.Amount.ToString()); + Assert.AreEqual("0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f", result.SwitchBlockHash); } } } \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/info-get-reward-v200.json b/Casper.Network.SDK.Test/TestData/info-get-reward-v200.json index 4565644..c00ffca 100644 --- a/Casper.Network.SDK.Test/TestData/info-get-reward-v200.json +++ b/Casper.Network.SDK.Test/TestData/info-get-reward-v200.json @@ -1 +1 @@ -{"api_version":"2.0.0","reward_amount":"62559062048560","era_id":13,"delegation_rate":1} +{"api_version":"2.0.0","reward_amount":"62559062048560","era_id":13,"delegation_rate":1, "switch_block_hash": "0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"} diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetRewardResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetRewardResult.cs index 0106c6a..f958775 100644 --- a/Casper.Network.SDK/JsonRpc/ResultTypes/GetRewardResult.cs +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetRewardResult.cs @@ -27,5 +27,11 @@ public class GetRewardResult : RpcResult /// [JsonPropertyName("delegation_rate")] public uint DelegationRate { get; init; } + + /// + /// The switch block hash at which the reward was distributed. + /// + [JsonPropertyName("switch_block_hash")] + public string SwitchBlockHash { get; init; } } } \ No newline at end of file From 0cb23cd1e271a720b5f00ac38ef922eaafbf7f9f Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 6 Aug 2024 09:07:42 +0200 Subject: [PATCH 080/126] fixed a multithreading problem with RPCLoggingHandler Signed-off-by: David Hernando --- Casper.Network.SDK/JsonRpc/RpcLoggingHandler.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Casper.Network.SDK/JsonRpc/RpcLoggingHandler.cs b/Casper.Network.SDK/JsonRpc/RpcLoggingHandler.cs index 27bbb0e..58b730a 100644 --- a/Casper.Network.SDK/JsonRpc/RpcLoggingHandler.cs +++ b/Casper.Network.SDK/JsonRpc/RpcLoggingHandler.cs @@ -12,8 +12,13 @@ namespace Casper.Network.SDK.JsonRpc /// public class RpcLoggingHandler : DelegatingHandler { - public StreamWriter LoggerStream { get; init; } - + private readonly TextWriter _loggerStream; + public TextWriter LoggerStream + { + get => _loggerStream; + init => _loggerStream = TextWriter.Synchronized(value); + } + private volatile bool _disposed; public RpcLoggingHandler(HttpMessageHandler innerHandler) From bc8db90366fdb546657764a16c255526cbec4bbc Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 6 Aug 2024 12:08:25 +0200 Subject: [PATCH 081/126] Updated examples Signed-off-by: David Hernando --- Docs/Examples/AwaitEvents/Program.cs | 18 +++- Docs/Examples/DelegateStake/Program.cs | 85 ---------------- Docs/Examples/GetAccountBalance/Program.cs | 43 --------- Docs/Examples/GetBalance/Program.cs | 50 ++++++++++ Docs/Examples/GetBlockTransfers/Program.cs | 17 ++-- .../GetChainspec.csproj} | 0 Docs/Examples/GetChainspec/Program.cs | 96 +++++++++++++++++++ Docs/Examples/GetNodeMetrics/Program.cs | 2 +- Docs/Examples/GetNodeStatus/Program.cs | 22 ++++- Docs/Examples/ListRewards/Program.cs | 30 ++++-- Docs/Examples/NativeAuction/Program.cs | 91 ++++++++++++++++++ .../Program.cs | 42 ++++---- 12 files changed, 320 insertions(+), 176 deletions(-) delete mode 100644 Docs/Examples/DelegateStake/Program.cs delete mode 100644 Docs/Examples/GetAccountBalance/Program.cs create mode 100644 Docs/Examples/GetBalance/Program.cs rename Docs/Examples/{DelegateStake/DelegateStake.csproj => GetChainspec/GetChainspec.csproj} (100%) create mode 100644 Docs/Examples/GetChainspec/Program.cs create mode 100644 Docs/Examples/NativeAuction/Program.cs rename Docs/Examples/{ExecTransfer => NativeTransfer}/Program.cs (62%) diff --git a/Docs/Examples/AwaitEvents/Program.cs b/Docs/Examples/AwaitEvents/Program.cs index 4ae7724..7ae3f1c 100644 --- a/Docs/Examples/AwaitEvents/Program.cs +++ b/Docs/Examples/AwaitEvents/Program.cs @@ -9,8 +9,8 @@ namespace Casper.NET.SDK.Examples public static class AwaitEvents { // Testnet node and port - private static string localNetHost = "52.35.59.254"; - private static int localNetPort = 9999; // use 18101 with NCTL + private static string localNetHost = "127.0.0.1"; + private static int localNetPort = 18101; static NetCasperClient casperSdk; @@ -39,7 +39,19 @@ public static void ListenEvents(int startFrom) else if (evt.EventType == EventType.BlockAdded) { var block = evt.Parse(); - Console.WriteLine("Block height: " + block.Block.Header.Height); + Console.WriteLine("Block height: " + block.Block.Height); + } + else if (evt.EventType == EventType.TransactionAccepted) + { + var transaction = evt.Parse(); + Console.WriteLine("TransactionAccepted: " + transaction.TransactionV1 != null + ? transaction.TransactionV1.Hash + : transaction.Deploy.Hash); + } + else if (evt.EventType == EventType.TransactionProcessed) + { + var transaction = evt.Parse(); + Console.WriteLine("TransactionProcessed: " + transaction.TransactionHash.ToString()); } else if (evt.EventType == EventType.DeployAccepted) { diff --git a/Docs/Examples/DelegateStake/Program.cs b/Docs/Examples/DelegateStake/Program.cs deleted file mode 100644 index 3f98aa7..0000000 --- a/Docs/Examples/DelegateStake/Program.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using System.Data; -using System.IO; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Casper.Network.SDK; -using Casper.Network.SDK.JsonRpc; -using Casper.Network.SDK.Types; -using Casper.Network.SDK.Utils; - -namespace CasperIntegrations -{ - public class DelegateStake - { - public static async Task Main(string[] args) - { - string nodeAddress = "http://52.35.59.254:7777/rpc"; - - try - { - // create an instance of the NetCasperClient that logs requests/outputs in stdout - // - var loggingHandler = new RpcLoggingHandler(new HttpClientHandler()) - { - LoggerStream = new StreamWriter(Console.OpenStandardOutput()) - }; - var casperSdk = new NetCasperClient(nodeAddress, loggingHandler); - - // load source account secret key from PEM files - // - var sourceKey = KeyPair.FromPem("./testnet1/secret_key.pem"); - - // choose a validator public key to stake the tokens - // - var validatorPK = PublicKey.FromHexString("017d96b9a63abcb61c870a4f55187a0a7ac24096bdb5fc585c12a686a4d892009e"); - - // create a hash key with the auction contract hash - // IMPORTANT: this value changes between network. Double check you - // are using the right contract hash before executing the deploy! - // - var contractHash = GlobalStateKey.FromString("hash-93d923e336b20a4c4ca14d592b60e5bd3fe330775618290104f9beb326db7ae2") as HashKey; - - // prepare a transfer deploy using the DelegateTokens template. - // Similarly, use the template UndelegateTokens to undelegate. - // - var deploy = DeployTemplates.DelegateTokens( - contractHash, - sourceKey.PublicKey, - validatorPK, - 100_000_000_000, - 3_000_000_000, - "casper-test"); - - // sign the deploy and send it to the network - // use the source account secret key for signing. - // - deploy.Sign(sourceKey); - - await casperSdk.PutDeploy(deploy); - - var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); - var deployResponse = await casperSdk.GetDeploy(deploy.Hash, tokenSource.Token); - - // only as an example, extract the transfer key, and retrieve the transfer - // information from the network - // - var result = deployResponse.Parse(); - - if(result.ExecutionResults[0].IsSuccess) - Console.WriteLine("Delegation successful"); - else - Console.WriteLine("Delegation completed with errors: " + result.ExecutionResults[0].ErrorMessage); - } - catch (RpcClientException e) - { - Console.WriteLine("ERROR:\n" + e.RpcError.Message); - } - catch (Exception e) - { - Console.WriteLine(e); - } - } - } -} diff --git a/Docs/Examples/GetAccountBalance/Program.cs b/Docs/Examples/GetAccountBalance/Program.cs deleted file mode 100644 index 8514fb3..0000000 --- a/Docs/Examples/GetAccountBalance/Program.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Threading.Tasks; -using Casper.Network.SDK; -using Casper.Network.SDK.JsonRpc; -using Casper.Network.SDK.Types; - -namespace Casper.NET.SDK.Examples -{ - public class GetAccountBalance - { - public static async Task Main(string[] args) - { - string nodeAddress = "http://52.35.59.254:7777/rpc"; - - var hex = "0203914289b334f57366541099a52156b149436fdb0422b3c48fe4115d0578abf690"; - var publicKey = PublicKey.FromHexString(hex); - - try - { - var casperSdk = new NetCasperClient(nodeAddress); - - // Get the balance using the account public key - // - var rpcResponse = await casperSdk.GetAccountBalance(publicKey); - Console.WriteLine("Public Key Balance: " + rpcResponse.Parse().BalanceValue); - - // Alternatively, use the main purse URef key to get the balance - // - var mainPurse = "uref-91e6278ea0ccc22eac98d1e6f2c6530f1e8a278eb1005c4b36e070b400c88301-007"; - var purseResponse = await casperSdk.GetAccountBalance(mainPurse); - Console.WriteLine("Purse Balance : " + purseResponse.Parse().BalanceValue); - } - catch (RpcClientException e) - { - Console.WriteLine("ERROR:\n" + e.RpcError.Message); - } - catch (Exception e) - { - Console.WriteLine(e); - } - } - } -} \ No newline at end of file diff --git a/Docs/Examples/GetBalance/Program.cs b/Docs/Examples/GetBalance/Program.cs new file mode 100644 index 0000000..f705da1 --- /dev/null +++ b/Docs/Examples/GetBalance/Program.cs @@ -0,0 +1,50 @@ +using System; +using System.Threading.Tasks; +using Casper.Network.SDK; +using Casper.Network.SDK.JsonRpc; +using Casper.Network.SDK.Types; + +namespace Casper.NET.SDK.Examples +{ + public class GetBalance + { + public static async Task Main(string[] args) + { + string nodeAddress = "http://127.0.0.1:11101/rpc"; + + var hex = "0184f6d260F4EE6869DDB36affe15456dE6aE045278FA2f467bb677561cE0daD55"; + var publicKey = PublicKey.FromHexString(hex); + + try + { + var casperSdk = new NetCasperClient(nodeAddress); + + // Get the balance using the account public key + // + var rpcResponse = await casperSdk.QueryBalance(publicKey); + Console.WriteLine("Public Key Balance : " + rpcResponse.Parse().BalanceValue); + + // Alternatively, use the main purse URef key to get the balance + // + var mainPurse = new URef("uref-0d0b57865e41b9e39170c038993997af432f66545f56838f1bf602c6d56e0e54-007"); + var purseResponse = await casperSdk.QueryBalance(mainPurse); + Console.WriteLine("Purse Balance : " + purseResponse.Parse().BalanceValue); + + // Or using an entity key + // + var entity = new AddressableEntityKey( + "entity-account-56befc13a6fd62e18f361700a5e08f966901c34df8041b36ec97d54d605c23de"); + var entityResponse = await casperSdk.QueryBalance(entity); + Console.WriteLine("Entity Balance : " + entityResponse.Parse().BalanceValue); + } + catch (RpcClientException e) + { + Console.WriteLine("ERROR:\n" + e.RpcError.Data); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + } +} \ No newline at end of file diff --git a/Docs/Examples/GetBlockTransfers/Program.cs b/Docs/Examples/GetBlockTransfers/Program.cs index 66d7adf..69fd675 100644 --- a/Docs/Examples/GetBlockTransfers/Program.cs +++ b/Docs/Examples/GetBlockTransfers/Program.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Casper.Network.SDK; using Casper.Network.SDK.JsonRpc; -using Casper.Network.SDK.Types; namespace Casper.NET.SDK.Examples { @@ -10,23 +9,21 @@ public class GetBlockTransfers { public static async Task Main(string[] args) { - string nodeAddress = "http://52.35.59.254:7777/rpc"; - string blockHash = "c7148e1e2e115d8fba357e04be2073d721847c982dc70d5c36b5f6d3cf66331c"; - int blockHeight = 20652; + string nodeAddress = "http://127.0.0.1:11101/rpc"; + + // find a block height with transfers using a block explorer + ulong blockHeight = 222; try { var casperSdk = new NetCasperClient(nodeAddress); - //Get latest block + // Get latest block transfers var rpcResponse = await casperSdk.GetBlockTransfers(); var transfers = rpcResponse.Parse().Transfers; Console.WriteLine("Number of transfers in latest block: " + transfers.Count); - //Get block by hash - rpcResponse = await casperSdk.GetBlockTransfers(blockHash); - - // //Get block by height + // Get block transfers by block height rpcResponse = await casperSdk.GetBlockTransfers(blockHeight); transfers = rpcResponse.Parse().Transfers; @@ -35,6 +32,8 @@ public static async Task Main(string[] args) foreach (var transfer in transfers) { Console.WriteLine("Transfer amount: " + transfer.Amount); + Console.WriteLine("Transfer from: " + transfer.From); + Console.WriteLine("Transfer to: " + transfer.To); } } catch (RpcClientException e) diff --git a/Docs/Examples/DelegateStake/DelegateStake.csproj b/Docs/Examples/GetChainspec/GetChainspec.csproj similarity index 100% rename from Docs/Examples/DelegateStake/DelegateStake.csproj rename to Docs/Examples/GetChainspec/GetChainspec.csproj diff --git a/Docs/Examples/GetChainspec/Program.cs b/Docs/Examples/GetChainspec/Program.cs new file mode 100644 index 0000000..7a8eaa9 --- /dev/null +++ b/Docs/Examples/GetChainspec/Program.cs @@ -0,0 +1,96 @@ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Threading.Tasks; +using Casper.Network.SDK; +using Casper.Network.SDK.JsonRpc; + +namespace Casper.NET.SDK.Examples +{ + class Chainspec + { + private Dictionary data; + + public Chainspec(string configContent) + { + data = new Dictionary(); + ParseContent(configContent); + } + + private void ParseContent(string content) + { + string currentSection = null; + var lines = content.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); + + foreach (var line in lines) + { + var trimmedLine = line.Trim(); + + if (string.IsNullOrEmpty(trimmedLine) || trimmedLine.StartsWith("#")) + continue; + + if (trimmedLine.StartsWith("[") && trimmedLine.EndsWith("]")) + { + // Section header + currentSection = trimmedLine.Substring(1, trimmedLine.Length - 2); + } + else if (currentSection != null && trimmedLine.Contains("=")) + { + var keyValue = trimmedLine.Split(new[] { '=' }, 2); + if (keyValue.Length == 2) + { + var key = keyValue[0].Trim(); + var value = keyValue[1].Trim().Trim('"'); + data[currentSection + "." + key] = value; + } + } + } + } + + public string GetValue(string key) + { + return data.GetValueOrDefault(key); + } + } + + public class GetChainspec + { + public static async Task Main(string[] args) + { + string nodeAddress = "http://127.0.0.1:11101/rpc"; + + try + { + // create an instance of the NetCasperClient that logs requests/outputs in stdout + // + var loggingHandler = new RpcLoggingHandler(new HttpClientHandler()) + { + LoggerStream = new StreamWriter(Console.OpenStandardOutput()) + }; + var casperSdk = new NetCasperClient(nodeAddress, loggingHandler); + + // get node status and print API version and PK as example + // + var rpcResponse = await casperSdk.GetChainspec(); + var result = rpcResponse.Parse(); + + var chainspec = new Chainspec(result.ChainspecBytes.ChainspecAsString); + + Console.WriteLine("Consensus protocol : " + chainspec.GetValue("core.consensus_protocol")); + Console.WriteLine("Pricing mode : " + chainspec.GetValue("core.pricing_handling.type")); + Console.WriteLine("Max gas price : " + chainspec.GetValue("vacancy.max_gas_price")); + Console.WriteLine("Wasm tx lanes : " + chainspec.GetValue("transactions.v1.wasm_lanes")); + } + catch (RpcClientException e) + { + Console.WriteLine("ERROR:\n" + e.RpcError.Message); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + } +} \ No newline at end of file diff --git a/Docs/Examples/GetNodeMetrics/Program.cs b/Docs/Examples/GetNodeMetrics/Program.cs index b5da45a..8f39ac4 100644 --- a/Docs/Examples/GetNodeMetrics/Program.cs +++ b/Docs/Examples/GetNodeMetrics/Program.cs @@ -10,7 +10,7 @@ public class GetNodeMetrics { public static async Task Main(string[] args) { - string nodeAddress = "http://52.35.59.254:8888/metrics"; + string nodeAddress = "http://127.0.0.1:14101/metrics"; try { diff --git a/Docs/Examples/GetNodeStatus/Program.cs b/Docs/Examples/GetNodeStatus/Program.cs index eb3d061..50ee0bc 100644 --- a/Docs/Examples/GetNodeStatus/Program.cs +++ b/Docs/Examples/GetNodeStatus/Program.cs @@ -1,9 +1,9 @@ using System; +using System.IO; using System.Net.Http; using System.Threading.Tasks; using Casper.Network.SDK; using Casper.Network.SDK.JsonRpc; -using Casper.Network.SDK.Types; namespace Casper.NET.SDK.Examples { @@ -11,13 +11,25 @@ public class GetNodeStatus { public static async Task Main(string[] args) { - string nodeAddress = "http://52.35.59.254:7777/rpc"; - + string nodeAddress = "http://127.0.0.1:11101/rpc"; + try { - var casperSdk = new NetCasperClient(nodeAddress); + // create an instance of the NetCasperClient that logs requests/outputs in stdout + // + var loggingHandler = new RpcLoggingHandler(new HttpClientHandler()) + { + LoggerStream = new StreamWriter(Console.OpenStandardOutput()) + }; + var casperSdk = new NetCasperClient(nodeAddress, loggingHandler); + + // get node status and print API version and PK as example + // var rpcResponse = await casperSdk.GetNodeStatus(); - Console.WriteLine(rpcResponse.Result.GetRawText()); + var nodeStatus = rpcResponse.Parse(); + + Console.WriteLine("API Version : " + nodeStatus.ApiVersion); + Console.WriteLine("Node PK : " + nodeStatus.OurPublicSigningKey); } catch (RpcClientException e) { diff --git a/Docs/Examples/ListRewards/Program.cs b/Docs/Examples/ListRewards/Program.cs index 82887a0..2b81736 100644 --- a/Docs/Examples/ListRewards/Program.cs +++ b/Docs/Examples/ListRewards/Program.cs @@ -4,16 +4,17 @@ using Casper.Network.SDK; using Casper.Network.SDK.JsonRpc; -namespace ListRewards +namespace Casper.NET.SDK.Examples { public static class ListRewards { - static string nodeAddress = "http://52.35.59.254:7777/rpc"; + static string nodeAddress = "http://127.0.0.1:11101/rpc"; + static NetCasperClient casperSdk; public async static Task GetEraSummary() { - var rpcResponse = await casperSdk.GetEraInfoBySwitchBlock(435_733); + var rpcResponse = await casperSdk.GetEraSummary(); var eraSummary = rpcResponse.Parse().EraSummary; Console.WriteLine("Block Hash: " + eraSummary.BlockHash); @@ -23,19 +24,30 @@ public async static Task GetEraSummary() // print the era rewards per validator // - Console.WriteLine("Validator Deleg. Rewards"); - Console.WriteLine("--------------------------------------------------------------------------------------------------------"); + Console.WriteLine("Validator"); + Console.WriteLine(" Delegator Rewards"); + Console.WriteLine("------------------------------------------------------------------------------------------------------------"); var groupedByValidator = eraInfo.SeigniorageAllocations .GroupBy(allocation => allocation.ValidatorPublicKey); foreach (var group in groupedByValidator) { - var eraRewards = group.Sum(a => (double)a.Amount); - eraRewards /= 1_000_000_000; + var rewards = group.Where(a => !a.IsDelegator).Sum(a => (double)a.Amount); + rewards /= 1_000_000_000; + + Console.WriteLine($"{group.Key} " + + $"{rewards.ToString("N9"),35} $CSPR"); - Console.WriteLine($"{group.Key} - {group.Count(),5} - " + - $"{eraRewards.ToString("N9"),20} $CSPR"); + var delegators = group.Where(a => a.IsDelegator) + .GroupBy(a => a.DelegatorPublicKey); + foreach (var delegatorAllocations in delegators) + { + var delegatorRewards = delegatorAllocations.Sum(a => (double)a.Amount); + Console.WriteLine(" " + delegatorAllocations.Key + " " + + $"{delegatorRewards.ToString("N9"),32} $CSPR"); + } + Console.WriteLine(); } } diff --git a/Docs/Examples/NativeAuction/Program.cs b/Docs/Examples/NativeAuction/Program.cs new file mode 100644 index 0000000..e760637 --- /dev/null +++ b/Docs/Examples/NativeAuction/Program.cs @@ -0,0 +1,91 @@ +using System; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Casper.Network.SDK; +using Casper.Network.SDK.JsonRpc; +using Casper.Network.SDK.Types; +using Casper.Network.SDK.Utils; + +namespace Casper.NET.SDK.Examples +{ + public class NativeAuction + { + public static async Task Main(string[] args) + { + string nodeAddress = "http://127.0.0.1:11101/rpc"; + string chainName = "casper-net-1"; + + try + { + // create an instance of the NetCasperClient that logs requests/outputs in stdout + // + var loggingHandler = new RpcLoggingHandler(new HttpClientHandler()) + { + LoggerStream = new StreamWriter(Console.OpenStandardOutput()) + }; + var casperSdk = new NetCasperClient(nodeAddress, loggingHandler); + + // load delegator account secret key from PEM files + // + var delegator = KeyPair.FromPem("./testnet1/secret_key.pem"); + + // choose a validator public key to stake the tokens + // + var validatorPK = PublicKey.FromHexString("01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6"); + + // prepare a delegate transaction using the transaction builder. + // + var transaction = new Transaction.NativeDelegateBuilder() + .From(delegator.PublicKey) + .Validator(validatorPK) + .Amount(1000_000_000_000) + .ChainName(chainName) + .Build(); + + // sign the transaction and send it to the network + // use the delegator account secret key for signing. + // + transaction.Sign(delegator); + + var response = await casperSdk.PutTransaction(transaction); + + // extract the transaction hash and use it to wait (up to 2mins) for the execution results + // + var transactionHash = response.GetTransactionHash(); + + var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); + var transactionResponse = await casperSdk.GetTransaction(transactionHash, tokenSource.Token); + + var executionResult = transactionResponse.Parse().ExecutionInfo.ExecutionResult; + + if (executionResult.IsSuccess) + { + Console.WriteLine("Delegation successful"); + + // only as an example, extract the Bid transform, + // and print the staked amount and the bonding purse + // + var transform = executionResult.Effect.First(t => t.Kind is WriteTransformKind kind && + kind.Value.BidKind is not null); + + var bid = (transform.Kind as WriteTransformKind).Value.BidKind; + Console.WriteLine("Staked amount : " + bid.Delegator.StakedAmount); + Console.WriteLine("Bonding purse : " + bid.Delegator.BondingPurse.ToString()); + } + else + Console.WriteLine("Delegation completed with errors: " + executionResult.ErrorMessage); + } + catch (RpcClientException e) + { + Console.WriteLine("ERROR:\n" + e.RpcError.Message); + } + catch (Exception e) + { + Console.WriteLine(e); + } + } + } +} diff --git a/Docs/Examples/ExecTransfer/Program.cs b/Docs/Examples/NativeTransfer/Program.cs similarity index 62% rename from Docs/Examples/ExecTransfer/Program.cs rename to Docs/Examples/NativeTransfer/Program.cs index 88ee31d..0cfd95f 100644 --- a/Docs/Examples/ExecTransfer/Program.cs +++ b/Docs/Examples/NativeTransfer/Program.cs @@ -8,14 +8,15 @@ using Casper.Network.SDK.Types; using Casper.Network.SDK.Utils; -namespace CasperIntegrations +namespace Casper.NET.SDK.Examples { - public class ExecAccountTransfer + public class NativeTransfer { public static async Task Main(string[] args) { - string nodeAddress = "http://52.35.59.254:7777/rpc"; - + string nodeAddress = "http://127.0.0.1:11101/rpc"; + string chainName = "casper-net-1"; + try { // create an instance of the NetCasperClient that logs requests/outputs in stdout @@ -31,36 +32,35 @@ public static async Task Main(string[] args) var sourceKey = KeyPair.FromPem("./testnet1/secret_key.pem"); var targetPK = PublicKey.FromPem("./testnet2/public_key.pem"); - // prepare a transfer deploy using the StandardTransfer template. + // prepare a transfer transaction using the transaction builder. // - var deploy = DeployTemplates.StandardTransfer( - sourceKey.PublicKey, - targetPK, - 25_000_000_000, - 100_000_000, - "casper-test"); + var transaction = new Transaction.NativeTransferBuilder() + .From(sourceKey.PublicKey) + .Target(targetPK) + .Amount(25_000_000_000) + .Id(DateUtils.ToEpochTime(DateTime.Now)) + .ChainName(chainName) + .GasPriceTolerance(1) + .Build(); - // sign the deploy and send it to the network + // sign the transaction and send it to the network // use the origin account secret key for signing. // - deploy.Sign(sourceKey); + transaction.Sign(sourceKey); - var response = await casperSdk.PutDeploy(deploy); + var response = await casperSdk.PutTransaction(transaction); - // extract the deploy hash and use it to wait (up to 2mins) for the execution results + // extract the transaction hash and use it to wait (up to 2mins) for the execution results // - var deployHash = response.GetDeployHash(); + var transactionHash = response.GetTransactionHash(); var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(120)); - var deployResponse = await casperSdk.GetDeploy(deployHash, tokenSource.Token); + var transactionResponse = await casperSdk.GetTransaction(transactionHash, tokenSource.Token); // only as an example, extract the transfer key, and retrieve the transfer // information from the network // - var transferKey = deployResponse.Parse().ExecutionResults[0].Transfers[0]; - - var queryResponse = await casperSdk.QueryGlobalState(transferKey); - var transfer = queryResponse.Parse().StoredValue.Transfer; + var transfer = transactionResponse.Parse().ExecutionInfo.ExecutionResult.Transfers[0]; Console.WriteLine("Transfer amount: " + transfer.Amount); From 561fc1e05968497e98be64281696e181d463db44 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 6 Aug 2024 12:14:22 +0200 Subject: [PATCH 082/126] Updated examples Signed-off-by: David Hernando --- Docs/Examples/GetBalance/GetBalance.csproj | 13 +++++++++++++ .../NativeAuction.csproj} | 1 + .../NativeTransfer.csproj} | 1 + 3 files changed, 15 insertions(+) create mode 100644 Docs/Examples/GetBalance/GetBalance.csproj rename Docs/Examples/{GetAccountBalance/GetAccountBalance.csproj => NativeAuction/NativeAuction.csproj} (84%) rename Docs/Examples/{ExecTransfer/ExecTransfer.csproj => NativeTransfer/NativeTransfer.csproj} (85%) diff --git a/Docs/Examples/GetBalance/GetBalance.csproj b/Docs/Examples/GetBalance/GetBalance.csproj new file mode 100644 index 0000000..622f8a9 --- /dev/null +++ b/Docs/Examples/GetBalance/GetBalance.csproj @@ -0,0 +1,13 @@ + + + + Exe + net8.0 + GetAccountBalance + + + + + + + diff --git a/Docs/Examples/GetAccountBalance/GetAccountBalance.csproj b/Docs/Examples/NativeAuction/NativeAuction.csproj similarity index 84% rename from Docs/Examples/GetAccountBalance/GetAccountBalance.csproj rename to Docs/Examples/NativeAuction/NativeAuction.csproj index 1dfa19e..1e4b1f6 100644 --- a/Docs/Examples/GetAccountBalance/GetAccountBalance.csproj +++ b/Docs/Examples/NativeAuction/NativeAuction.csproj @@ -3,6 +3,7 @@ Exe net8.0 + DelegateStake diff --git a/Docs/Examples/ExecTransfer/ExecTransfer.csproj b/Docs/Examples/NativeTransfer/NativeTransfer.csproj similarity index 85% rename from Docs/Examples/ExecTransfer/ExecTransfer.csproj rename to Docs/Examples/NativeTransfer/NativeTransfer.csproj index 1dfa19e..a36b02d 100644 --- a/Docs/Examples/ExecTransfer/ExecTransfer.csproj +++ b/Docs/Examples/NativeTransfer/NativeTransfer.csproj @@ -3,6 +3,7 @@ Exe net8.0 + ExecTransfer From 9e6eb851f6a5beeb3d2a1d0c33e6c71bcfac487e Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 20 Aug 2024 14:38:04 +0200 Subject: [PATCH 083/126] fixed AwaitEvents example Signed-off-by: David Hernando --- Docs/Examples/AwaitEvents/Program.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Docs/Examples/AwaitEvents/Program.cs b/Docs/Examples/AwaitEvents/Program.cs index 7ae3f1c..155a0d1 100644 --- a/Docs/Examples/AwaitEvents/Program.cs +++ b/Docs/Examples/AwaitEvents/Program.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Casper.Network.SDK; using Casper.Network.SDK.SSE; -using Casper.Network.SDK.Types; namespace Casper.NET.SDK.Examples { @@ -44,9 +43,7 @@ public static void ListenEvents(int startFrom) else if (evt.EventType == EventType.TransactionAccepted) { var transaction = evt.Parse(); - Console.WriteLine("TransactionAccepted: " + transaction.TransactionV1 != null - ? transaction.TransactionV1.Hash - : transaction.Deploy.Hash); + Console.WriteLine("TransactionAccepted: " + transaction.Hash); } else if (evt.EventType == EventType.TransactionProcessed) { From d0c223cd037a8dc64026fadb0ff1cb8007f07f6e Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 19 Nov 2024 17:27:02 +0100 Subject: [PATCH 084/126] WIP Signed-off-by: David Hernando --- .../RPCResponses/GetTransactionTest.cs | 14 +- .../ByteSerializers/BaseByteSerializer.cs | 7 + .../ByteSerializers/CalltableSerialization.cs | 76 +++++ .../ByteSerializers/LittleEndianConverter.cs | 27 ++ .../TransactionV1ByteSerializer.cs | 198 ------------ Casper.Network.SDK/Types/Block.cs | 4 +- Casper.Network.SDK/Types/InitiatorAddr.cs | 33 +- Casper.Network.SDK/Types/PricingMode.cs | 126 ++++++-- Casper.Network.SDK/Types/Transaction.cs | 38 +-- .../Types/TransactionBuilder.cs | 17 +- Casper.Network.SDK/Types/TransactionV1.cs | 138 +++------ Casper.Network.SDK/Types/TransactionV1Body.cs | 43 --- .../Types/TransactionV1EntryPoint.cs | 60 ++++ .../Types/TransactionV1Header.cs | 59 ---- .../Types/TransactionV1Payload.cs | 291 ++++++++++++++++++ .../Types/TransactionV1Scheduling.cs | 124 +++----- .../Types/TransactionV1Target.cs | 256 +++++++-------- 17 files changed, 835 insertions(+), 676 deletions(-) create mode 100644 Casper.Network.SDK/ByteSerializers/CalltableSerialization.cs create mode 100644 Casper.Network.SDK/ByteSerializers/LittleEndianConverter.cs delete mode 100644 Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs delete mode 100644 Casper.Network.SDK/Types/TransactionV1Body.cs delete mode 100644 Casper.Network.SDK/Types/TransactionV1Header.cs create mode 100644 Casper.Network.SDK/Types/TransactionV1Payload.cs diff --git a/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs index 40b6173..2452f49 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs @@ -90,10 +90,9 @@ public void GetTransactionWithSessionTransactionTest_v200() Assert.IsNotNull(transactionV1); Assert.AreEqual(transactionV1.Hash, transaction.Hash); - Assert.AreEqual("9b1d4002bb1af17ec9fc4193ac5ba7db6535b6dd813034b730829a94b13ebc46", transactionV1.Header.BodyHash); - Assert.AreEqual(transactionV1.Header.ChainName, transaction.ChainName); - Assert.AreEqual(transactionV1.Header.Timestamp, transaction.Timestamp); - Assert.AreEqual(transactionV1.Header.InitiatorAddr.PublicKey, transaction.InitiatorAddr.PublicKey); + Assert.AreEqual(transactionV1.Payload.ChainName, transaction.ChainName); + Assert.AreEqual(transactionV1.Payload.Timestamp, transaction.Timestamp); + Assert.AreEqual(transactionV1.Payload.InitiatorAddr.PublicKey, transaction.InitiatorAddr.PublicKey); Assert.IsNull(transaction.InitiatorAddr.AccountHash); Assert.IsTrue(transaction.Invocation is Transaction.SessionTransactionInvocation); Assert.AreEqual("01020304", Hex.ToHexString((transaction.Invocation as Transaction.SessionTransactionInvocation)!.Wasm)); @@ -126,10 +125,9 @@ public void GetTransactionWithStoredTransactionTest_v200() Assert.IsNotNull(transactionV1); Assert.AreEqual(transactionV1.Hash, transaction.Hash); - Assert.AreEqual("07ed52f990a206153d2a29f6a42eb754009c4b120981dd3fd0842d4057ee7484", transactionV1.Header.BodyHash); - Assert.AreEqual(transactionV1.Header.ChainName, transaction.ChainName); - Assert.AreEqual(transactionV1.Header.Timestamp, transaction.Timestamp); - Assert.AreEqual(transactionV1.Header.InitiatorAddr.AccountHash, transaction.InitiatorAddr.AccountHash); + Assert.AreEqual(transactionV1.Payload.ChainName, transaction.ChainName); + Assert.AreEqual(transactionV1.Payload.Timestamp, transaction.Timestamp); + Assert.AreEqual(transactionV1.Payload.InitiatorAddr.AccountHash, transaction.InitiatorAddr.AccountHash); Assert.AreEqual("account-hash-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f", transaction.InitiatorAddr.AccountHash.ToString().ToLower()); Assert.IsNull(transaction.InitiatorAddr.PublicKey); Assert.IsTrue(transaction.Invocation is Transaction.StoredTransactionInvocation); diff --git a/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs b/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs index 3dff02b..037ec0a 100644 --- a/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs +++ b/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs @@ -12,6 +12,13 @@ protected static void WriteInteger(MemoryStream ms, int value) ms.Write(bytes); } + protected static void WriteUShort(MemoryStream ms, ushort value) + { + var bytes = BitConverter.GetBytes(value); + if(!BitConverter.IsLittleEndian) Array.Reverse(bytes); + ms.Write(bytes); + } + protected static void WriteUInteger(MemoryStream ms, uint value) { var bytes = BitConverter.GetBytes(value); diff --git a/Casper.Network.SDK/ByteSerializers/CalltableSerialization.cs b/Casper.Network.SDK/ByteSerializers/CalltableSerialization.cs new file mode 100644 index 0000000..c73cdd1 --- /dev/null +++ b/Casper.Network.SDK/ByteSerializers/CalltableSerialization.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Casper.Network.SDK.Types; + +namespace Casper.Network.SDK.ByteSerializers +{ + public class Field + { + public readonly ushort Index; + public readonly uint Offset; + public readonly byte[] Value; + + public Field(ushort index, uint offset, byte[] value) + { + this.Index = index; + this.Offset = offset; + this.Value = value; + } + + public static int SerializedVecSize(int numberOfFields) + { + return sizeof(uint) + sizeof(uint) * sizeof(ushort); + } + } + + public class CalltableSerialization : BaseByteSerializer + { + private readonly List _fields = new List(); + private int _currentOffset; + + public CalltableSerialization() + { + _fields = new List(); + } + + public CalltableSerialization AddField(int index, CLValue value) + { + // var serializer = new CLValueByteSerializer(); + // var bytes = serializer.ToBytes(value); + return AddField(index, value.Bytes); + } + + public CalltableSerialization AddField(int index, byte[] value) + { + if (_fields.Count != index) { + throw new Exception("Add fields in correct index order."); + } + + _fields.Add(new Field((ushort)index, (uint)_currentOffset, value)); + + _currentOffset += value.Length; + + return this; + } + + public byte[] GetBytes() + { + var calltable_bytes = new MemoryStream(); + var payload_bytes = new MemoryStream(); + WriteUInteger(calltable_bytes, (uint)_fields.Count); + foreach (var field in _fields) + { + WriteUShort(calltable_bytes, field.Index); + WriteUInteger(calltable_bytes, field.Offset); + payload_bytes.Write(field.Value, 0, field.Value.Length); + } + WriteUInteger(calltable_bytes, (uint)payload_bytes.Length); + payload_bytes.Seek(0, SeekOrigin.Begin); + payload_bytes.CopyTo(calltable_bytes); + + return calltable_bytes.ToArray(); + } + } +} diff --git a/Casper.Network.SDK/ByteSerializers/LittleEndianConverter.cs b/Casper.Network.SDK/ByteSerializers/LittleEndianConverter.cs new file mode 100644 index 0000000..ac4db5f --- /dev/null +++ b/Casper.Network.SDK/ByteSerializers/LittleEndianConverter.cs @@ -0,0 +1,27 @@ +using System; + +namespace Casper.Network.SDK.ByteSerializers +{ + public class LittleEndianConverter + { + public static byte[] GetBytes(int value) + { + var bytes = BitConverter.GetBytes(value); + if (!BitConverter.IsLittleEndian) + Array.Reverse(bytes); + return bytes; + } + + public static int ToInt32(byte[] bytes, int startIndex = 0) + { + if (!BitConverter.IsLittleEndian) + { + var temp = new byte[4]; + Array.Copy(bytes, startIndex, temp, 0, 4); + Array.Reverse(temp); // Convert to native order + return BitConverter.ToInt32(temp, 0); + } + return BitConverter.ToInt32(bytes, startIndex); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs b/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs deleted file mode 100644 index 8c37d8d..0000000 --- a/Casper.Network.SDK/ByteSerializers/TransactionV1ByteSerializer.cs +++ /dev/null @@ -1,198 +0,0 @@ -using System; -using System.IO; -using Casper.Network.SDK.Types; -using Org.BouncyCastle.Utilities.Encoders; - -namespace Casper.Network.SDK.ByteSerializers -{ - public class TransactionV1ByteSerializer : BaseByteSerializer, IByteSerializer - { - public byte[] ToBytes(ITransactionV1Target source) - { - var ms = new MemoryStream(); - - switch (source) - { - case NativeTransactionV1Target: - WriteByte(ms, (byte)TransactionTargetType.Native); - break; - case StoredTransactionV1Target storedTarget: - WriteByte(ms, (byte)TransactionTargetType.Stored); - switch (storedTarget.Id) - { - case ByHashInvocationTarget byHash: - WriteByte(ms, (byte)InvocationTargetTag.ByHash); - WriteBytes(ms, Hex.Decode(byHash.Hash)); - break; - case ByNameInvocationTarget byName: - WriteByte(ms, (byte)InvocationTargetTag.ByName); - WriteString(ms, byName.Name); - break; - case ByPackageHashInvocationTarget byPackageHash: - WriteByte(ms, (byte)InvocationTargetTag.ByPackageHash); - WriteBytes(ms, Hex.Decode(byPackageHash.Hash)); - WriteMaybeUInteger(ms, byPackageHash.Version); - break; - case ByPackageNameInvocationTarget byPackageName: - WriteByte(ms, (byte)InvocationTargetTag.ByPackageName); - WriteString(ms, byPackageName.Name); - WriteMaybeUInteger(ms, byPackageName.Version); - break; - } - WriteByte(ms, (byte)storedTarget.Runtime); - break; - case SessionTransactionV1Target sessionTarget: - { - WriteByte(ms, (byte)TransactionTargetType.Session); - - if (sessionTarget.ModuleBytes == null || sessionTarget.ModuleBytes.Length == 0) - WriteInteger(ms, 0); - else - { - WriteInteger(ms, sessionTarget.ModuleBytes.Length); - WriteBytes(ms, sessionTarget.ModuleBytes); - } - - WriteByte(ms, (byte)sessionTarget.Runtime); - break; - } - } - - return ms.ToArray(); - } - - public byte[] ToBytes(ITransactionV1EntryPoint source) - { - var ms = new MemoryStream(); - - switch (source) - { - case CustomTransactionV1EntryPoint customEntryPoint: - WriteByte(ms, 0x00); - WriteString(ms, customEntryPoint.Name); - break; - case NativeTransactionV1EntryPoint nativeEntryPoint: - WriteByte(ms, (byte)nativeEntryPoint.Type); - break; - default: - throw new Exception("Cannot serialize empty TransactionEntryPoint to bytes"); - } - - return ms.ToArray(); - } - - public byte[] ToBytes(ITransactionV1Scheduling source) - { - var ms = new MemoryStream(); - - switch (source) - { - case StandardTransactionV1Scheduling: - WriteByte(ms, (byte)TransactionV1SchedulingType.Standard); - break; - case FutureEraTransactionV1Scheduling eraScheduling: - WriteByte(ms, (byte)TransactionV1SchedulingType.FutureEra); - WriteULong(ms, eraScheduling.EraId); - break; - case FutureTimestampTransactionV1Scheduling timestampScheduling: - WriteByte(ms, (byte)TransactionV1SchedulingType.FutureTimestamp); - WriteULong(ms, timestampScheduling.Timestamp); - break; - } - - return ms.ToArray(); - } - - public byte[] ToBytes(TransactionV1Body source) - { - var ms = new MemoryStream(); - - var namedArgSerializer = new NamedArgByteSerializer(); - - ms.Write(BitConverter.GetBytes(source.RuntimeArgs.Count)); - foreach (var args in source.RuntimeArgs) - WriteBytes(ms, namedArgSerializer.ToBytes(args)); - - WriteBytes(ms, ToBytes(source.Target)); - WriteBytes(ms, ToBytes(source.EntryPoint)); - WriteByte(ms, (byte)source.Category); - WriteBytes(ms, ToBytes(source.Scheduling)); - - return ms.ToArray(); - } - - public byte[] ToBytes(IPricingMode source) - { - var ms = new MemoryStream(); - - switch (source) - { - case ClassicPricingMode classicPricingMode: - ms.WriteByte((byte)PricingModeType.Classic); - WriteULong(ms, (ulong)classicPricingMode.PaymentAmount); - WriteByte(ms, (byte)classicPricingMode.GasPriceTolerance); - WriteByte(ms, (byte)(classicPricingMode.StandardPayment ? 0x01 : 0x00)); - break; - case FixedPricingMode fixedPricingMode: - ms.WriteByte((byte)PricingModeType.Fixed); - WriteByte(ms, (byte)fixedPricingMode.GasPriceTolerance); - break; - case ReservedPricingMode reservedPricingMode: - ms.WriteByte((byte)PricingModeType.Reserved); - WriteBytes(ms, Hex.Decode(reservedPricingMode.Receipt)); - break; - } - - return ms.ToArray(); - } - - public byte[] ToBytes(InitiatorAddr source) - { - var ms = new MemoryStream(); - - if (source.PublicKey != null) - { - WriteByte(ms, 0x00); - WriteBytes(ms, source.PublicKey.GetBytes()); - } - else if (source.AccountHash != null) - { - WriteByte(ms, 0x01); - WriteBytes(ms, source.AccountHash.RawBytes); - } - - return ms.ToArray(); - } - - public byte[] ToBytes(TransactionV1Header source) - { - var ms = new MemoryStream(); - WriteString(ms, source.ChainName); - WriteULong(ms, source.Timestamp); - WriteULong(ms, source.Ttl); - WriteBytes(ms, Hex.Decode(source.BodyHash)); - WriteBytes(ms, ToBytes(source.PricingMode)); - WriteBytes(ms, ToBytes(source.InitiatorAddr)); - return ms.ToArray(); - } - - public byte[] ToBytes(TransactionV1 source) - { - var ms = new MemoryStream(); - - WriteBytes(ms, Hex.Decode(source.Hash)); - - WriteBytes(ms, ToBytes(source.Header)); - - WriteBytes(ms, ToBytes(source.Body)); - // add the approvals - // - var approvalSerializer = new DeployApprovalByteSerializer(); - WriteInteger(ms, source.Approvals.Count); - foreach (var approval in source.Approvals) - WriteBytes(ms, approvalSerializer.ToBytes(approval)); - - return ms.ToArray(); - } - } -} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Block.cs b/Casper.Network.SDK/Types/Block.cs index 9d49db0..1108044 100644 --- a/Casper.Network.SDK/Types/Block.cs +++ b/Casper.Network.SDK/Types/Block.cs @@ -736,13 +736,13 @@ public override void Write( case 1: writer.WritePropertyName("Version1"); writer.WriteStartObject(); - JsonSerializer.Serialize((BlockV1)block, options); + JsonSerializer.Serialize(writer, (BlockV1)block, options); writer.WriteEndObject(); break; case 2: writer.WritePropertyName("Version2"); writer.WriteStartObject(); - JsonSerializer.Serialize(block, options); + JsonSerializer.Serialize(writer, block, options); writer.WriteEndObject(); break; default: diff --git a/Casper.Network.SDK/Types/InitiatorAddr.cs b/Casper.Network.SDK/Types/InitiatorAddr.cs index 41380b9..63514e2 100644 --- a/Casper.Network.SDK/Types/InitiatorAddr.cs +++ b/Casper.Network.SDK/Types/InitiatorAddr.cs @@ -1,4 +1,6 @@ +using System; using System.Text.Json.Serialization; +using Casper.Network.SDK.ByteSerializers; namespace Casper.Network.SDK.Types { @@ -51,18 +53,37 @@ public override string ToString() { return PublicKey != null ? PublicKey.ToString() - : (AccountHash != null - ? AccountHash.ToString() - : null); + : AccountHash?.ToString(); } public string ToHexString() { return PublicKey != null ? PublicKey.ToString() - : (AccountHash != null - ? AccountHash.ToHexString() - : null); + : AccountHash?.ToHexString(); + } + + const ushort TAG_FIELD_INDEX = 0; + const byte PUBLIC_KEY_VARIANT_TAG = 0; + const ushort PUBLIC_KEY_FIELD_INDEX = 1; + const byte ACCOUNT_HASH_VARIANT_TAG = 1; + const ushort ACCOUNT_HASH_FIELD_INDEX = 1; + + public byte[] ToBytes() + { + if(PublicKey != null) + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, new byte[] { PUBLIC_KEY_VARIANT_TAG }) + .AddField(PUBLIC_KEY_FIELD_INDEX, CLValue.PublicKey(PublicKey)) + .GetBytes(); + + if(AccountHash != null) + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, new byte[] { ACCOUNT_HASH_VARIANT_TAG }) + .AddField(PUBLIC_KEY_FIELD_INDEX, CLValue.Key(AccountHash)) + .GetBytes(); + + throw new Exception("Unable to serialize initiator addr"); } } } diff --git a/Casper.Network.SDK/Types/PricingMode.cs b/Casper.Network.SDK/Types/PricingMode.cs index 08e454b..ffb6404 100644 --- a/Casper.Network.SDK/Types/PricingMode.cs +++ b/Casper.Network.SDK/Types/PricingMode.cs @@ -1,16 +1,18 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; +using Casper.Network.SDK.ByteSerializers; +using Org.BouncyCastle.Utilities.Encoders; namespace Casper.Network.SDK.Types { -public enum PricingModeType + public enum PricingModeType { /// /// The original payment model, where the creator of the transaction specifies how much they will pay, /// at what gas price. /// - Classic = 0, + PaymentLimited = 0, /// /// The cost of the transaction is determined by the cost table, per the transaction category. @@ -21,41 +23,59 @@ public enum PricingModeType /// The payment for this transaction was previously reserved, as proven by the receipt hash /// (this is for future use, not currently supported by the Casper network). /// - Reserved = 2, + Prepaid = 2, } public interface IPricingMode { #if NET7_0_OR_GREATER - public bool IsClassic => this is ClassicPricingMode; + public bool IsPaymentLimited => this is PaymentLimitedPricingMode; public bool IsFixed => this is FixedPricingMode; - public bool IsReserved => this is ReservedPricingMode; + public bool IsReserved => this is PrepaidPricingMode; #endif + + public byte[] ToBytes(); } - - public class ClassicPricingMode: IPricingMode + + public class PaymentLimitedPricingMode : IPricingMode { /// /// Payment amount. /// [JsonPropertyName("payment_amount")] public ulong PaymentAmount { get; init; } - + /// /// Standard payment. /// [JsonPropertyName("standard_payment")] public bool StandardPayment { get; init; } - + /// /// User-specified gas_price tolerance (minimum 1). This is interpreted to mean "do not include this /// transaction in a block if the current gas price is greater than this number". /// [JsonPropertyName("gas_price_tolerance")] public byte GasPriceTolerance { get; init; } + + const ushort TAG_FIELD_INDEX = 0; + const byte PAYMENT_LIMITED_VARIANT_TAG = 0; + const ushort PAYMENT_LIMITED_PAYMENT_AMOUNT_INDEX = 1; + const ushort PAYMENT_LIMITED_GAS_PRICE_TOLERANCE_INDEX = 2; + const ushort PAYMENT_LIMITED_STANDARD_PAYMENT_INDEX = 3; + + public byte[] ToBytes() + { + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, CLValue.U8(PAYMENT_LIMITED_VARIANT_TAG)) + .AddField(PAYMENT_LIMITED_PAYMENT_AMOUNT_INDEX, CLValue.U64(PaymentAmount)) + .AddField(PAYMENT_LIMITED_GAS_PRICE_TOLERANCE_INDEX, CLValue.U8(GasPriceTolerance)) + .AddField(PAYMENT_LIMITED_STANDARD_PAYMENT_INDEX, CLValue.Bool(StandardPayment)) + .GetBytes(); + } } - - public class FixedPricingMode: IPricingMode + + public class FixedPricingMode : IPricingMode { /// /// User-specified gas_price tolerance (minimum 1). This is interpreted to mean "do not include this @@ -63,17 +83,54 @@ public class FixedPricingMode: IPricingMode /// [JsonPropertyName("gas_price_tolerance")] public byte GasPriceTolerance { get; init; } + + /// + /// User-specified additional computation factor (minimum 0). If "0" is provided, + /// no additional logic is applied to the computation limit. Each value above "0" + /// tells the node that it needs to treat the transaction as if it uses more gas + /// than it's serialized size indicates. Each "1" will increase the "wasm lane" + /// size bucket for this transaction by 1. So if the size of the transaction + /// indicates bucket "0" and "additional_computation_factor = 2", the transaction + /// will be treated as a "2". + /// + [JsonPropertyName("additional_computation_factor")] + public byte AdditionalComputationFactor { get; init; } + + const ushort TAG_FIELD_INDEX = 0; + const byte FIXED_VARIANT_TAG = 1; + const ushort FIXED_GAS_PRICE_TOLERANCE_INDEX = 1; + const ushort FIXED_ADDITIONAL_COMPUTATION_FACTOR_INDEX = 2; + + public byte[] ToBytes() + { + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, CLValue.U8(FIXED_VARIANT_TAG)) + .AddField(FIXED_GAS_PRICE_TOLERANCE_INDEX, CLValue.U8(GasPriceTolerance)) + .AddField(FIXED_ADDITIONAL_COMPUTATION_FACTOR_INDEX, CLValue.U8(AdditionalComputationFactor)) + .GetBytes(); + } } - - public class ReservedPricingMode: IPricingMode + + public class PrepaidPricingMode : IPricingMode { /// - /// Pre-paid receipt in the Reserved Pricing mode. + /// Pre-paid receipt in the Prepaid Pricing mode. /// [JsonPropertyName("receipt")] public string Receipt { get; init; } + + const ushort TAG_FIELD_INDEX = 0; + const byte RESERVED_VARIANT_TAG = 2; + const ushort RESERVED_RECEIPT_INDEX = 1; + public byte[] ToBytes() + { + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, CLValue.U8(RESERVED_VARIANT_TAG)) + .AddField(RESERVED_RECEIPT_INDEX, Hex.Decode(Receipt)) + .GetBytes(); + } } - + /// /// Pricing mode of a Transaction. /// @@ -86,9 +143,9 @@ public class PricingMode /// Amount in motes to pay for the transaction. /// Defaults to 1. Gas price tolerance admitted. /// Defaults to true. Indicates if this is a standar payment (non-standard payments are handled via wasm code). - public static IPricingMode Classic(ulong paymentAmount, byte gasPriceTolerance = 1, bool standardPayment = true) + public static IPricingMode PaymentLimited(ulong paymentAmount, byte gasPriceTolerance = 1, bool standardPayment = true) { - return new ClassicPricingMode() + return new PaymentLimitedPricingMode() { StandardPayment = standardPayment, GasPriceTolerance = gasPriceTolerance, @@ -100,26 +157,28 @@ public static IPricingMode Classic(ulong paymentAmount, byte gasPriceTolerance = /// The cost of the transaction is determined by the cost table, per the transaction category. /// /// Defaults to 1. Gas price tolerance admitted. - public static IPricingMode Fixed(byte gasPriceTolerance = 1) + /// Defaults to 0. Each unit increases the wasm lane the transaction is executed in.. + public static IPricingMode Fixed(byte gasPriceTolerance = 1, byte additionalComputationFactor = 0) { return new FixedPricingMode() { GasPriceTolerance = gasPriceTolerance, + AdditionalComputationFactor = additionalComputationFactor, }; } /// - /// The payment for this transaction was previously reserved, as proven by the receipt hash. + /// The payment for this transaction was previously pre-paid, as proven by the receipt hash. /// /// Pre-paid receipt. - public static IPricingMode Reserved(string receipt) + public static IPricingMode Prepaid(string receipt) { - return new ReservedPricingMode() + return new PrepaidPricingMode() { Receipt = receipt, }; } - + public class PricingModeConverter : JsonConverter { public override IPricingMode Read( @@ -128,7 +187,7 @@ public override IPricingMode Read( JsonSerializerOptions options) { IPricingMode pricingMode = null; - + if (reader.TokenType != JsonTokenType.StartObject) throw new JsonException("Cannot deserialize PricingMode. StartObject expected"); reader.Read(); @@ -136,26 +195,27 @@ public override IPricingMode Read( if (reader.TokenType != JsonTokenType.PropertyName) throw new JsonException("Cannot deserialize PricingMode. PropertyName expected"); - if(!EnumCompat.TryParse(reader.GetString(), out PricingModeType pricingModeType)) + if (!EnumCompat.TryParse(reader.GetString(), out PricingModeType pricingModeType)) throw new Exception($"Unknown pricing mode '{reader.GetString()}'"); reader.Read(); if (reader.TokenType != JsonTokenType.StartObject) throw new JsonException("Cannot deserialize PricingMode. StartObject expected"); - + switch (pricingModeType) { - case PricingModeType.Classic: - pricingMode = JsonSerializer.Deserialize(ref reader, options); + case PricingModeType.PaymentLimited: + pricingMode = JsonSerializer.Deserialize(ref reader, options); break; case PricingModeType.Fixed: pricingMode = JsonSerializer.Deserialize(ref reader, options); break; - case PricingModeType.Reserved: - pricingMode = JsonSerializer.Deserialize(ref reader, options); + case PricingModeType.Prepaid: + pricingMode = JsonSerializer.Deserialize(ref reader, options); break; } + reader.Read(); return pricingMode; @@ -168,9 +228,9 @@ public override void Write( { switch (value) { - case ClassicPricingMode classicPricingMode: + case PaymentLimitedPricingMode classicPricingMode: writer.WriteStartObject(); - writer.WritePropertyName("Classic"); + writer.WritePropertyName("PaymentLimited"); JsonSerializer.Serialize(writer, classicPricingMode); writer.WriteEndObject(); break; @@ -180,9 +240,9 @@ public override void Write( JsonSerializer.Serialize(writer, fixedPricingMode); writer.WriteEndObject(); break; - case ReservedPricingMode reservedPricingMode: + case PrepaidPricingMode reservedPricingMode: writer.WriteStartObject(); - writer.WritePropertyName("Reserved"); + writer.WritePropertyName("Prepaid"); JsonSerializer.Serialize(writer, reservedPricingMode); writer.WriteEndObject(); break; diff --git a/Casper.Network.SDK/Types/Transaction.cs b/Casper.Network.SDK/Types/Transaction.cs index 8ebbc7e..20f105a 100644 --- a/Casper.Network.SDK/Types/Transaction.cs +++ b/Casper.Network.SDK/Types/Transaction.cs @@ -241,11 +241,11 @@ public static explicit operator Transaction(Deploy deploy) { var amountArg = paymentModule.RuntimeArgs.FirstOrDefault(arg => arg.Name == "amount"); if (amountArg == null) - pricingMode = Types.PricingMode.Classic(0, (byte)deploy.Header.GasPrice, false); + pricingMode = Types.PricingMode.PaymentLimited(0, (byte)deploy.Header.GasPrice, false); else { var paymentAmount = (ulong)amountArg.Value.ToBigInteger(); - pricingMode = Types.PricingMode.Classic(paymentAmount, (byte)deploy.Header.GasPrice, paymentModule.ModuleBytes == null); + pricingMode = Types.PricingMode.PaymentLimited(paymentAmount, (byte)deploy.Header.GasPrice, paymentModule.ModuleBytes == null); } } else @@ -274,35 +274,32 @@ public static explicit operator Transaction(TransactionV1 transactionV1) { ITransactionInvocation transactionInvocation; - if(transactionV1.Body.Target is NativeTransactionV1Target && - transactionV1.Body.EntryPoint is NativeTransactionV1EntryPoint nativeEntryPoint) + if(transactionV1.Payload.Target is NativeTransactionV1Target && + transactionV1.Payload.EntryPoint is NativeTransactionV1EntryPoint nativeEntryPoint) { transactionInvocation = new NativeTransactionInvocation() { Type = nativeEntryPoint.Type, - RuntimeArgs = transactionV1.Body.RuntimeArgs, - Category = transactionV1.Body.Category, + RuntimeArgs = transactionV1.Payload.RuntimeArgs, }; } - else if (transactionV1.Body.Target is StoredTransactionV1Target storedTarget && - transactionV1.Body.EntryPoint is CustomTransactionV1EntryPoint customEntryPoint) + else if (transactionV1.Payload.Target is StoredTransactionV1Target storedTarget && + transactionV1.Payload.EntryPoint is CustomTransactionV1EntryPoint customEntryPoint) { transactionInvocation = new StoredTransactionInvocation() { InvocationTarget = storedTarget.Id, EntryPoint = customEntryPoint.Name, - RuntimeArgs = transactionV1.Body.RuntimeArgs, - Category = transactionV1.Body.Category, + RuntimeArgs = transactionV1.Payload.RuntimeArgs, }; } - else if (transactionV1.Body.Target is SessionTransactionV1Target sessionTarget && - transactionV1.Body.EntryPoint.Name.Equals("Call", StringComparison.InvariantCultureIgnoreCase)) + else if (transactionV1.Payload.Target is SessionTransactionV1Target sessionTarget && + transactionV1.Payload.EntryPoint.Name.Equals("Call", StringComparison.InvariantCultureIgnoreCase)) { transactionInvocation = new SessionTransactionInvocation() { Wasm = sessionTarget.ModuleBytes, - RuntimeArgs = transactionV1.Body.RuntimeArgs, - Category = transactionV1.Body.Category, + RuntimeArgs = transactionV1.Payload.RuntimeArgs, }; } else @@ -315,14 +312,13 @@ public static explicit operator Transaction(TransactionV1 transactionV1) _transactionV1 = transactionV1, _version = TransactionVersion.TransactionV1, Hash = transactionV1.Hash, - InitiatorAddr = transactionV1.Header.InitiatorAddr, - Timestamp = transactionV1.Header.Timestamp, - Ttl = transactionV1.Header.Ttl, - ChainName = transactionV1.Header.ChainName, - PricingMode = transactionV1.Header.PricingMode, + InitiatorAddr = transactionV1.Payload.InitiatorAddr, + Timestamp = transactionV1.Payload.Timestamp, + Ttl = transactionV1.Payload.Ttl, + ChainName = transactionV1.Payload.ChainName, + PricingMode = transactionV1.Payload.PricingMode, Approvals = transactionV1.Approvals, - Scheduling = transactionV1.Body.Scheduling, - Category = transactionV1.Body.Category, + Scheduling = transactionV1.Payload.Scheduling, Invocation = transactionInvocation, }; } diff --git a/Casper.Network.SDK/Types/TransactionBuilder.cs b/Casper.Network.SDK/Types/TransactionBuilder.cs index 9d8f7ba..8b12b59 100644 --- a/Casper.Network.SDK/Types/TransactionBuilder.cs +++ b/Casper.Network.SDK/Types/TransactionBuilder.cs @@ -59,24 +59,19 @@ public T GasPriceTolerance(byte gasPriceTolerance) public virtual TransactionV1 Build() { - var body = new TransactionV1Body() - { - RuntimeArgs = _runtimeArgs, - Target = _invocationTarget, - EntryPoint = _entryPoint, - Scheduling = _scheduling, - Category = _category, - }; - var header = new TransactionV1Header() + var payload = new TransactionV1Payload() { InitiatorAddr = _from, Timestamp = DateUtils.ToEpochTime(_timestamp.HasValue ? _timestamp.Value : DateTime.UtcNow), Ttl = _ttl, ChainName = _chainName, PricingMode = Types.PricingMode.Fixed(_gasPriceTolerance), + RuntimeArgs = _runtimeArgs, + Target = _invocationTarget, + EntryPoint = _entryPoint, + Scheduling = _scheduling, }; - var transaction = new TransactionV1(header, body); - return transaction; + return new TransactionV1(payload); } } diff --git a/Casper.Network.SDK/Types/TransactionV1.cs b/Casper.Network.SDK/Types/TransactionV1.cs index 7990462..9b57315 100644 --- a/Casper.Network.SDK/Types/TransactionV1.cs +++ b/Casper.Network.SDK/Types/TransactionV1.cs @@ -20,25 +20,19 @@ public class TransactionV1 /// [JsonPropertyName("hash")] public string Hash { get; } - + /// /// List of signers and signatures for this transaction. /// - [JsonPropertyName("approvals")] + [JsonPropertyName("approvals")] public List Approvals { get; } = new List(); - - /// - /// Header for this transaction. - /// - [JsonPropertyName("header")] - public TransactionV1Header Header { get; init; } /// - /// Body for this transaction. + /// Payload for this transaction. /// - [JsonPropertyName("body")] - public TransactionV1Body Body { get; init; } - + [JsonPropertyName("payload")] + public TransactionV1Payload Payload { get; init; } + /// /// Loads and deserializes a TransactionV1 from a file. /// @@ -47,7 +41,7 @@ public static TransactionV1 Load(string filename) var data = File.ReadAllText(filename); return TransactionV1.Parse(data); } - + /// /// Parses a Transaction from a string with json. /// @@ -67,7 +61,7 @@ public static TransactionV1 Parse(string json) throw new Exception(message); } } - + /// /// Saves a transaction object to a file. /// @@ -75,7 +69,7 @@ public void Save(string filename) { File.WriteAllText(filename, JsonSerializer.Serialize(this)); } - + /// /// Returns a json string with the transaction. /// @@ -83,36 +77,31 @@ public string SerializeToJson() { return JsonSerializer.Serialize(this); } - + [JsonConstructor] public TransactionV1(string hash, - TransactionV1Header header, - TransactionV1Body body, + TransactionV1Payload payload, List approvals) { this.Hash = hash; - this.Header = header; - this.Body = body; + this.Payload = payload; this.Approvals = approvals; } - - public TransactionV1(TransactionV1Header header, - TransactionV1Body body) + + public TransactionV1(TransactionV1Payload payload) { - var bodyHash = ComputeBodyHash(body); - this.Header = new TransactionV1Header() - { - ChainName = header.ChainName, - Timestamp = header.Timestamp, - Ttl = header.Ttl, - BodyHash = Hex.ToHexString(bodyHash), - PricingMode = header.PricingMode, - InitiatorAddr = header.InitiatorAddr, - }; - this.Hash = Hex.ToHexString(ComputeHeaderHash(this.Header)); - this.Body = body; + var payloadBytes = payload.ToBytes(); + var blake2BDigest = new Org.BouncyCastle.Crypto.Digests.Blake2bDigest(256); + + blake2BDigest.BlockUpdate(payloadBytes, 0, payloadBytes.Length); + + var hash = new byte[blake2BDigest.GetDigestSize()]; + blake2BDigest.DoFinal(hash, 0); + + this.Payload = payload; + this.Hash = Hex.ToHexString(hash); } - + /// /// Signs the transaction with a private key and adds a new Approval to it. /// @@ -126,7 +115,7 @@ public void Sign(KeyPair keyPair) Signer = keyPair.PublicKey }); } - + /// /// Adds an approval to the transaction. No check is done to the approval signature. /// @@ -134,34 +123,25 @@ public void AddApproval(Approval approval) { this.Approvals.Add(approval); } - + /// - /// Validates the body and header hashes in the transaction. + /// Validates the transaction hash. /// /// output string with a validation error message if validation fails. empty otherwise. - /// false if the validation of hashes is not successful + /// false if the validation of hash is not successful public bool ValidateHashes(out string message) { - var computedHash = ComputeBodyHash(this.Body); - if (!Hex.Decode(this.Header.BodyHash).SequenceEqual(computedHash)) - { - message = "Computed Body Hash does not match value in transaction header. " + - $"Expected: '{this.Header.BodyHash}'. " + - $"Computed: '{computedHash}'."; - return false; - } - - computedHash = ComputeHeaderHash(this.Header); + var computedHash = this.ToBytes(); if (!Hex.Decode(this.Hash).SequenceEqual(computedHash)) { - message = "Computed Hash does not match value in transaction object. " + + message = "Computed hash does not match value in transaction object." + $"Expected: '{this.Hash}'. " + $"Computed: '{computedHash}'."; return false; } message = ""; - return true; + return false; } /// @@ -172,61 +152,41 @@ public bool ValidateHashes(out string message) public bool VerifySignatures(out string message) { message = string.Empty; + + var decodedHash = Hex.Decode(this.Hash); - foreach (var approval in Approvals) + foreach (var approval in Approvals.Where(approval => !approval.Signer + .VerifySignature(decodedHash, approval.Signature.RawBytes))) { - if (!approval.Signer.VerifySignature(Hex.Decode(this.Hash), - approval.Signature.RawBytes)) - { - message = $"Error verifying signature with signer '{approval.Signer}'."; - return false; - } + message = $"Error verifying signature with signer '{approval.Signer}'."; + return false; } return true; } - + /// /// returns the number of bytes resulting from the binary serialization of the Deploy. /// public int GetTransactionSizeInBytes() { - var serializer = new TransactionV1ByteSerializer(); - return serializer.ToBytes(this).Length; + return this.ToBytes().Length; } - - private byte[] ComputeBodyHash(TransactionV1Body body) - { - var ms = new MemoryStream(); - - var serializer = new TransactionV1ByteSerializer(); - - ms.Write(serializer.ToBytes(body)); - - var bcBl2bdigest = new Org.BouncyCastle.Crypto.Digests.Blake2bDigest(256); - var bBody = ms.ToArray(); - bcBl2bdigest.BlockUpdate(bBody, 0, bBody.Length); - - var hash = new byte[bcBl2bdigest.GetDigestSize()]; - bcBl2bdigest.DoFinal(hash, 0); - - return hash; - } - - private byte[] ComputeHeaderHash(TransactionV1Header header) + public byte[] ToBytes() { - var serializer = new TransactionV1ByteSerializer(); - var bHeader = serializer.ToBytes(header); + var ms = new MemoryStream(); - var bcBl2bdigest = new Org.BouncyCastle.Crypto.Digests.Blake2bDigest(256); + ms.Write(Hex.Decode(this.Hash)); - bcBl2bdigest.BlockUpdate(bHeader, 0, bHeader.Length); + ms.Write(this.Payload.ToBytes()); - var hash = new byte[bcBl2bdigest.GetDigestSize()]; - bcBl2bdigest.DoFinal(hash, 0); + var approvalSerializer = new DeployApprovalByteSerializer(); + ms.Write(LittleEndianConverter.GetBytes(this.Approvals.Count)); + foreach (var approval in this.Approvals) + ms.Write(approvalSerializer.ToBytes(approval)); - return hash; + return ms.ToArray(); } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionV1Body.cs b/Casper.Network.SDK/Types/TransactionV1Body.cs deleted file mode 100644 index 0cb5c43..0000000 --- a/Casper.Network.SDK/Types/TransactionV1Body.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; -using Casper.Network.SDK.Converters; - -namespace Casper.Network.SDK.Types -{ - /// - /// Body of a TransactionV1. - /// - public class TransactionV1Body - { - /// - /// List of runtime arguments. - /// - [JsonPropertyName("args")] - [JsonConverter(typeof(GenericListConverter))] - public List RuntimeArgs { get; init; } - - /// - /// Entry point or method of the contract to call. - /// - [JsonPropertyName("entry_point")] - [JsonConverter(typeof(TransactionV1EntryPoint.TransactionEntryPointConverter))] - public ITransactionV1EntryPoint EntryPoint { get; init; } - - /// - /// Target contract of the transaction (native, custom or session). - /// - [JsonPropertyName("target")] - [JsonConverter(typeof(TransactionV1Target.TransactionTargetConverter))] - public ITransactionV1Target Target { get; init; } - - /// - /// Scheduling of the transaction. - /// - [JsonPropertyName("scheduling")] - [JsonConverter(typeof(TransactionV1Scheduling.TransactionV1SchedulingConverter))] - public ITransactionV1Scheduling Scheduling { get; init; } - - [JsonPropertyName("transaction_category")] - public TransactionCategory Category { get; init; } - } -} diff --git a/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs b/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs index 176842f..6c30e84 100644 --- a/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs +++ b/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; +using Casper.Network.SDK.ByteSerializers; namespace Casper.Network.SDK.Types { @@ -54,12 +55,24 @@ public enum NativeEntryPoint /// Used to call entry point call() in session transactions /// Call = 9, + + /// + /// The `add_reservations` native entry point, used to add delegators to validator's reserve list. + /// + AddReservations = 10, + + /// + /// The `cancel_reservations` native entry point, used to remove delegators from validator's reserve list. + /// + CancelReservations = 11, } public interface ITransactionV1EntryPoint { public string Name { get; } + + public byte[] ToBytes(); } public class NativeTransactionV1EntryPoint : ITransactionV1EntryPoint @@ -88,6 +101,41 @@ public NativeTransactionV1EntryPoint(string name) throw new Exception($"Invalid name for a TransactionV1NativeEntryPoint ({name})."); } } + + const ushort TAG_FIELD_INDEX = 0; + const byte CALL_VARIANT_TAG = 1; + const byte TRANSFER_VARIANT_TAG = 2; + const byte ADD_BID_VARIANT_TAG = 3; + const byte WITHDRAW_BID_VARIANT_TAG = 4; + const byte DELEGATE_VARIANT_TAG = 5; + const byte UNDELEGATE_VARIANT_TAG = 6; + const byte REDELEGATE_VARIANT_TAG = 7; + const byte ACTIVATE_BID_VARIANT_TAG = 8; + const byte CHANGE_BID_PUBLIC_KEY_VARIANT_TAG = 9; + const byte ADD_RESERVATIONS_VARIANT_TAG = 10; + const byte CANCEL_RESERVATIONS_VARIANT_TAG = 11; + + public byte[] ToBytes() + { + var tag = Type switch + { + NativeEntryPoint.Call => CALL_VARIANT_TAG, + NativeEntryPoint.Transfer => TRANSFER_VARIANT_TAG, + NativeEntryPoint.AddBid => ADD_BID_VARIANT_TAG, + NativeEntryPoint.Delegate => DELEGATE_VARIANT_TAG, + NativeEntryPoint.WithdrawBid => WITHDRAW_BID_VARIANT_TAG, + NativeEntryPoint.Undelegate => UNDELEGATE_VARIANT_TAG, + NativeEntryPoint.Redelegate => REDELEGATE_VARIANT_TAG, + NativeEntryPoint.ActivateBid => ACTIVATE_BID_VARIANT_TAG, + NativeEntryPoint.ChangeBidPublicKey => CHANGE_BID_PUBLIC_KEY_VARIANT_TAG, + NativeEntryPoint.AddReservations => ADD_RESERVATIONS_VARIANT_TAG, + NativeEntryPoint.CancelReservations => CANCEL_RESERVATIONS_VARIANT_TAG, + _ => throw new Exception($"Unknown NativeEntryPoint type: {Type}") + }; + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, new byte[] {tag}) + .GetBytes(); + } } public class CustomTransactionV1EntryPoint : ITransactionV1EntryPoint @@ -98,6 +146,18 @@ public CustomTransactionV1EntryPoint(string name) { Name = name; } + + const ushort TAG_FIELD_INDEX = 0; + const byte CUSTOM_VARIANT_TAG = 1; + const ushort CUSTOM_CUSTOM_INDEX = 1; + + public byte[] ToBytes() + { + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, new byte[] {CUSTOM_VARIANT_TAG}) + .AddField(CUSTOM_CUSTOM_INDEX, CLValue.String(Name)) + .GetBytes(); + } } /// diff --git a/Casper.Network.SDK/Types/TransactionV1Header.cs b/Casper.Network.SDK/Types/TransactionV1Header.cs deleted file mode 100644 index 3ef32f5..0000000 --- a/Casper.Network.SDK/Types/TransactionV1Header.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; -using Casper.Network.SDK.Converters; -using Casper.Network.SDK.Utils; - -namespace Casper.Network.SDK.Types -{ - /// - /// The header portion of a TransactionV1. - /// - public class TransactionV1Header - { - /// - /// The address of the initiator of a transaction. - /// - [JsonPropertyName("initiator_addr")] - public InitiatorAddr InitiatorAddr { get; set; } - - /// - /// Timestamp formatted as per RFC 3339 - /// - [JsonPropertyName("timestamp")] - [JsonConverter(typeof(DateTime2EpochConverter))] - public ulong Timestamp { get; set; } - - /// - /// Duration of the Deploy in milliseconds (from timestamp). - /// - [JsonPropertyName("ttl")] - [JsonConverter(typeof(HumanizeTTLConverter))] - public ulong Ttl { get; set; } - - /// - /// Pricing mode of a Transaction. - /// - [JsonPropertyName("pricing_mode")] - [JsonConverter(typeof(PricingMode.PricingModeConverter))] - public IPricingMode PricingMode { get; set; } - - /// - /// Hash of the body part of this Deploy. - /// - [JsonPropertyName("body_hash")] - public string BodyHash { get; set; } - - /// - /// Name of the chain where the deploy is executed. - /// - [JsonPropertyName("chain_name")] - public string ChainName { get; set; } - - public TransactionV1Header() - { - Timestamp = DateUtils.ToEpochTime(DateTime.UtcNow); - Ttl = 1800000; - } - } -} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionV1Payload.cs b/Casper.Network.SDK/Types/TransactionV1Payload.cs new file mode 100644 index 0000000..8815224 --- /dev/null +++ b/Casper.Network.SDK/Types/TransactionV1Payload.cs @@ -0,0 +1,291 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using Casper.Network.SDK.ByteSerializers; +using Casper.Network.SDK.Converters; +using Casper.Network.SDK.Utils; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Casper.Network.SDK.Types +{ + [JsonConverter(typeof(PayloadFieldsConverter))] + public class PayloadFields : BaseByteSerializer + { + private readonly Dictionary _fields = new Dictionary(); + + public void AddField(ushort field, byte[] value) + { + _fields.Add(field, value); + } + + public class PayloadFieldsConverter : JsonConverter + { + public override PayloadFields Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + var payload = new PayloadFields(); + try + { + reader.Read(); // skip start object + while (reader.TokenType == JsonTokenType.PropertyName) + { + var fieldKey = reader.GetString(); + if (ushort.TryParse(fieldKey, out var field)) + { + reader.Read(); + var value = reader.GetString(); + payload._fields.Add(field, Hex.Decode(value)); + } + } + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + + return payload; + } + + const ushort ARGS_MAP_KEY = 0; + const ushort TARGET_MAP_KEY = 1; + const ushort ENTRY_POINT_MAP_KEY = 2; + const ushort SCHEDULING_MAP_KEY = 3; + + public override void Write( + Utf8JsonWriter writer, + PayloadFields payload, + JsonSerializerOptions options) + { + writer.WriteStartObject(); + if(payload._fields.TryGetValue(ARGS_MAP_KEY, out var argsMapField)) + writer.WriteString(ARGS_MAP_KEY.ToString(), Hex.ToHexString(argsMapField)); + if(payload._fields.TryGetValue(TARGET_MAP_KEY, out var targetMapField)) + writer.WriteString(TARGET_MAP_KEY.ToString(), Hex.ToHexString(targetMapField)); + if(payload._fields.TryGetValue(ENTRY_POINT_MAP_KEY, out var entryPointMapField)) + writer.WriteString(ENTRY_POINT_MAP_KEY.ToString(), Hex.ToHexString(entryPointMapField)); + if(payload._fields.TryGetValue(SCHEDULING_MAP_KEY, out var schedulingMapField)) + writer.WriteString(SCHEDULING_MAP_KEY.ToString(), Hex.ToHexString(schedulingMapField)); + writer.WriteEndObject(); + } + } + public byte[] ToBytes() + { + var fields_bytes = new MemoryStream(); + WriteInteger(fields_bytes, _fields.Count); + foreach (var field in _fields) + { + WriteUShort(fields_bytes, field.Key); + var bytes = field.Value; + WriteBytes(fields_bytes, bytes); + } + return fields_bytes.ToArray(); + } + } + + internal class TransactionV1PayloadJson + { + [JsonPropertyName("initiator_addr")] + public InitiatorAddr InitiatorAddr { get; set; } + + [JsonPropertyName("timestamp")] + [JsonConverter(typeof(DateTime2EpochConverter))] + public ulong Timestamp { get; set; } + + + /// + /// Duration of the Deploy in milliseconds (from timestamp). + /// + [JsonPropertyName("ttl")] + [JsonConverter(typeof(HumanizeTTLConverter))] + public ulong Ttl { get; set; } + + /// + /// Pricing mode of a Transaction. + /// + [JsonPropertyName("pricing_mode")] + [JsonConverter(typeof(PricingMode.PricingModeConverter))] + public IPricingMode PricingMode { get; set; } + + /// + /// Name of the chain where the deploy is executed. + /// + [JsonPropertyName("chain_name")] + public string ChainName { get; set; } + + [JsonPropertyName("fields")] + [JsonConverter(typeof(PayloadFields.PayloadFieldsConverter))] + public PayloadFields Fields { get; set; } + + public TransactionV1PayloadJson() + { + Fields = new PayloadFields(); + } + } + + /// + /// The payload portion of a TransactionV1. + /// + [JsonConverter(typeof(TransactionV1PayloadConverter))] + public class TransactionV1Payload + { + /// + /// The address of the initiator of a transaction. + /// + [JsonPropertyName("initiator_addr")] + public InitiatorAddr InitiatorAddr { get; set; } + + /// + /// Timestamp formatted as per RFC 3339 + /// + [JsonPropertyName("timestamp")] + [JsonConverter(typeof(DateTime2EpochConverter))] + public ulong Timestamp { get; set; } + + /// + /// Duration of the Deploy in milliseconds (from timestamp). + /// + [JsonPropertyName("ttl")] + [JsonConverter(typeof(HumanizeTTLConverter))] + public ulong Ttl { get; set; } + + /// + /// Pricing mode of a Transaction. + /// + [JsonPropertyName("pricing_mode")] + [JsonConverter(typeof(PricingMode.PricingModeConverter))] + public IPricingMode PricingMode { get; set; } + + /// + /// Name of the chain where the deploy is executed. + /// + [JsonPropertyName("chain_name")] + public string ChainName { get; set; } + + // [JsonPropertyName("fields")] + // public PayloadFields Fields { get; set; } + + /// + /// List of runtime arguments. + /// + [JsonIgnore] + public List RuntimeArgs { get; init; } + + /// + /// Entry point or method of the contract to call. + /// + [JsonIgnore] + public ITransactionV1EntryPoint EntryPoint { get; init; } + + /// + /// Target contract of the transaction (native, custom or session). + /// + [JsonIgnore] + public ITransactionV1Target Target { get; init; } + + /// + /// Scheduling of the transaction. + /// + [JsonIgnore] + public ITransactionV1Scheduling Scheduling { get; init; } + + public TransactionV1Payload() + { + Timestamp = DateUtils.ToEpochTime(DateTime.UtcNow); + Ttl = 1800000; + } + + public class TransactionV1PayloadConverter : JsonConverter + { + public override TransactionV1Payload Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + reader.Read(); // skip start object + var payloadJson = JsonSerializer.Deserialize(ref reader, options); + + return new TransactionV1Payload() + { + InitiatorAddr = payloadJson.InitiatorAddr, + Timestamp = payloadJson.Timestamp, + Ttl = payloadJson.Ttl, + PricingMode = payloadJson.PricingMode, + ChainName = payloadJson.ChainName, + }; //TODO: Deserialize fields!!! + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + TransactionV1Payload payload, + JsonSerializerOptions options) + { + var payloadJson = new TransactionV1PayloadJson() + { + InitiatorAddr = payload.InitiatorAddr, + Timestamp = payload.Timestamp, + Ttl = payload.Ttl, + PricingMode = payload.PricingMode, + ChainName = payload.ChainName, + }; + var ms = new MemoryStream(); + var namedArgSerializer = new NamedArgByteSerializer(); + ms.Write(BitConverter.GetBytes(payload.RuntimeArgs.Count)); + foreach (var args in payload.RuntimeArgs) + ms.Write(namedArgSerializer.ToBytes(args)); + payloadJson.Fields.AddField(0, ms.ToArray()); + payloadJson.Fields.AddField(1, payload.Target.ToBytes()); + payloadJson.Fields.AddField(2, payload.EntryPoint.ToBytes()); + payloadJson.Fields.AddField(3, payload.Scheduling.ToBytes()); + JsonSerializer.Serialize(writer, payloadJson, options); + } + } + + const ushort INITIATOR_ADDR_FIELD_INDEX = 0; + const ushort TIMESTAMP_FIELD_INDEX = 1; + const ushort TTL_FIELD_INDEX = 2; + const ushort CHAIN_NAME_FIELD_INDEX = 3; + const ushort PRICING_MODE_FIELD_INDEX = 4; + const ushort FIELDS_FIELD_INDEX = 5; + + const ushort ARGS_MAP_KEY = 0; + const ushort TARGET_MAP_KEY = 1; + const ushort ENTRY_POINT_MAP_KEY = 2; + const ushort SCHEDULING_MAP_KEY = 3; + + public byte[] ToBytes() + { + var ms = new MemoryStream(); + var namedArgSerializer = new NamedArgByteSerializer(); + ms.Write(BitConverter.GetBytes(RuntimeArgs.Count)); + foreach (var args in RuntimeArgs) + ms.Write(namedArgSerializer.ToBytes(args)); + + var fields = new PayloadFields(); + fields.AddField(ARGS_MAP_KEY, ms.ToArray()); + fields.AddField(TARGET_MAP_KEY, Target.ToBytes()); + fields.AddField(ENTRY_POINT_MAP_KEY, EntryPoint.ToBytes()); + fields.AddField(SCHEDULING_MAP_KEY, Scheduling.ToBytes()); + + return new CalltableSerialization() + .AddField(INITIATOR_ADDR_FIELD_INDEX, InitiatorAddr.ToBytes()) + .AddField(TIMESTAMP_FIELD_INDEX, CLValue.U64(Timestamp)) + .AddField(TTL_FIELD_INDEX, CLValue.U64(Ttl)) + .AddField(CHAIN_NAME_FIELD_INDEX, CLValue.String(ChainName)) + .AddField(PRICING_MODE_FIELD_INDEX, PricingMode.ToBytes()) + .AddField(FIELDS_FIELD_INDEX, fields.ToBytes()) + .GetBytes(); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionV1Scheduling.cs b/Casper.Network.SDK/Types/TransactionV1Scheduling.cs index 2c4281b..4af7687 100644 --- a/Casper.Network.SDK/Types/TransactionV1Scheduling.cs +++ b/Casper.Network.SDK/Types/TransactionV1Scheduling.cs @@ -1,23 +1,43 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; +using Casper.Network.SDK.ByteSerializers; using Casper.Network.SDK.Utils; namespace Casper.Network.SDK.Types { public enum TransactionV1SchedulingType { + /// + /// No special scheduling applied. + /// Standard = 0, + /// + /// Execution should be scheduled for the specified era. + /// FutureEra = 1, + /// + /// Execution should be scheduled for the specified timestamp or later. + /// FutureTimestamp = 2, } public interface ITransactionV1Scheduling { + public byte[] ToBytes(); } public class StandardTransactionScheduling : ITransactionV1Scheduling { + const ushort TAG_FIELD_INDEX = 0; + const byte STANDARD_VARIANT = 0; + + public byte[] ToBytes() + { + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, new byte[] { STANDARD_VARIANT }) + .GetBytes(); + } } public class StandardTransactionV1Scheduling : StandardTransactionScheduling @@ -26,7 +46,19 @@ public class StandardTransactionV1Scheduling : StandardTransactionScheduling public class FutureEraTransactionScheduling : ITransactionV1Scheduling { + const ushort TAG_FIELD_INDEX = 0; + const byte FUTURE_ERA_VARIANT = 1; + const ushort FUTURE_ERA_ERA_ID_INDEX = 1; + public ulong EraId { get; init; } + + public byte[] ToBytes() + { + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, new byte[] { FUTURE_ERA_VARIANT } ) + .AddField(FUTURE_ERA_ERA_ID_INDEX, CLValue.U64(EraId)) + .GetBytes(); + } } public class FutureEraTransactionV1Scheduling : FutureEraTransactionScheduling @@ -36,6 +68,18 @@ public class FutureEraTransactionV1Scheduling : FutureEraTransactionScheduling public class FutureTimestampTransactionScheduling : ITransactionV1Scheduling { public ulong Timestamp { get; init; } + + const ushort TAG_FIELD_INDEX = 0; + const byte FUTURE_TIMESTAMP_VARIANT = 2; + const ushort FUTURE_TIMESTAMP_TIMESTAMP_INDEX = 1; + + public byte[] ToBytes() + { + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, new byte[] { FUTURE_TIMESTAMP_VARIANT }) + .AddField(FUTURE_TIMESTAMP_TIMESTAMP_INDEX, CLValue.U64(Timestamp)) + .GetBytes(); + } } public class FutureTimestampTransactionV1Scheduling : FutureTimestampTransactionScheduling @@ -66,84 +110,6 @@ public static ITransactionV1Scheduling FutureTimestamp(ulong timestamp) public class TransactionV1Scheduling : TransactionScheduling { - public class TransactionV1SchedulingConverter : JsonConverter - { - public override ITransactionV1Scheduling Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - ITransactionV1Scheduling v1Scheduling; - - if (reader.TokenType == JsonTokenType.String) - { - var schedulingType = reader.GetString(); - switch (schedulingType) - { - case "Standard": - v1Scheduling = new StandardTransactionV1Scheduling(); - break; - default: - throw new JsonException( - "Cannot deserialize TransactionScheduling. Unknown scheduling type"); - } - } - else if (reader.TokenType == JsonTokenType.StartObject) - { - reader.Read(); // skip start object - var schedulingType = reader.GetString(); - reader.Read(); - switch (schedulingType) - { - case "FutureEra": - v1Scheduling = new FutureEraTransactionV1Scheduling - { - EraId = reader.GetUInt64(), - }; - break; - case "FutureTimestamp": - v1Scheduling = new FutureTimestampTransactionV1Scheduling - { - Timestamp = DateUtils.ToEpochTime(reader.GetString()), - }; - break; - default: - throw new JsonException( - "Cannot deserialize TransactionScheduling. Unknown scheduling type"); - } - - reader.Read(); - } - else - throw new JsonException("Cannot deserialize TransactionScheduling."); - - return v1Scheduling; - } - - public override void Write( - Utf8JsonWriter writer, - ITransactionV1Scheduling value, - JsonSerializerOptions options) - { - switch (value) - { - case StandardTransactionV1Scheduling: - writer.WriteStringValue("Standard"); - break; - case FutureEraTransactionV1Scheduling eraScheduling: - writer.WriteStartObject(); - writer.WriteNumber("FutureEra", eraScheduling.EraId); - writer.WriteEndObject(); - break; - case FutureTimestampTransactionV1Scheduling timestampScheduling: - writer.WriteStartObject(); - writer.WriteString("FutureTimestamp", DateUtils.ToISOString(timestampScheduling.Timestamp)); - writer.WriteEndObject(); - break; - default: - throw new JsonException("Cannot serialize due to unkown transaction scheduling type."); - } - } - } + } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionV1Target.cs b/Casper.Network.SDK/Types/TransactionV1Target.cs index 85714f4..f531536 100644 --- a/Casper.Network.SDK/Types/TransactionV1Target.cs +++ b/Casper.Network.SDK/Types/TransactionV1Target.cs @@ -1,6 +1,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; +using Casper.Network.SDK.ByteSerializers; using Casper.Network.SDK.Converters; using Org.BouncyCastle.Utilities.Encoders; @@ -17,11 +18,35 @@ public enum InvocationTargetTag public class ByHashInvocationTarget : IInvocationTarget { public string Hash { get; init; } + + const ushort TAG_FIELD_INDEX = 0; + const byte BY_HASH_VARIANT = 0; + const ushort BY_HASH_HASH_INDEX = 1; + + public byte[] ToBytes() + { + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, new byte[] { BY_HASH_VARIANT }) + .AddField(BY_HASH_HASH_INDEX, Hex.Decode(Hash)) + .GetBytes(); + } } public class ByNameInvocationTarget : IInvocationTarget { public string Name { get; init; } + + const ushort TAG_FIELD_INDEX = 0; + const byte BY_NAME_VARIANT = 1; + const ushort BY_NAME_NAME_INDEX = 1; + + public byte[] ToBytes() + { + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, new byte[] { BY_NAME_VARIANT }) + .AddField(BY_NAME_NAME_INDEX, CLValue.String(Name)) + .GetBytes(); + } } public class ByPackageHashInvocationTarget : IInvocationTarget @@ -29,6 +54,22 @@ public class ByPackageHashInvocationTarget : IInvocationTarget [JsonPropertyName("addr")] public string Hash { get; init; } [JsonPropertyName("version")] public UInt32? Version { get; init; } + + const ushort TAG_FIELD_INDEX = 0; + const byte BY_PACKAGE_HASH_VARIANT = 2; + const ushort BY_PACKAGE_HASH_ADDR_INDEX = 1; + const ushort BY_PACKAGE_HASH_VERSION_INDEX = 2; + + public byte[] ToBytes() + { + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, new byte[] { BY_PACKAGE_HASH_VARIANT }) + .AddField(BY_PACKAGE_HASH_ADDR_INDEX, Hex.Decode(Hash)) + .AddField(BY_PACKAGE_HASH_VERSION_INDEX, Version.HasValue + ? CLValue.Option(CLValue.U32(Version.Value)) + : CLValue.OptionNone(CLType.U32)) + .GetBytes(); + } } public class ByPackageNameInvocationTarget : IInvocationTarget @@ -36,6 +77,21 @@ public class ByPackageNameInvocationTarget : IInvocationTarget [JsonPropertyName("name")] public string Name { get; init; } [JsonPropertyName("version")] public UInt32? Version { get; init; } + + const ushort TAG_FIELD_INDEX = 0; + const byte BY_PACKAGE_NAME_VARIANT = 3; + const ushort BY_PACKAGE_NAME_NAME_INDEX = 1; + const ushort BY_PACKAGE_NAME_VERSION_INDEX = 2; + + public byte[] ToBytes() + { + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, new byte[] { BY_PACKAGE_NAME_VARIANT }) + .AddField(BY_PACKAGE_NAME_NAME_INDEX, CLValue.String(Name)) + .AddField(BY_PACKAGE_NAME_VERSION_INDEX, Version.HasValue + ? CLValue.Option(CLValue.U32(Version.Value)) + : CLValue.OptionNone(CLType.U32)).GetBytes(); + } } [JsonConverter(typeof(InvocationTargetConverter))] @@ -111,6 +167,8 @@ public override void Write( writer.WriteEndObject(); } } + + public byte[] ToBytes(); } public enum TransactionTargetType @@ -135,10 +193,20 @@ public enum TransactionRuntime public interface ITransactionV1Target { + public byte[] ToBytes(); } public class NativeTransactionV1Target : ITransactionV1Target { + const ushort TAG_FIELD_INDEX = 0; + const byte NATIVE_VARIANT = 0; + + public byte[] ToBytes() + { + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, new byte[] { NATIVE_VARIANT }) + .GetBytes(); + } } public class StoredTransactionV1Target : ITransactionV1Target @@ -151,10 +219,37 @@ public class StoredTransactionV1Target : ITransactionV1Target /// [JsonPropertyName("runtime")] public TransactionRuntime Runtime { get; set; } + + /// + /// The amount of motes to transfer before code is executed. + /// + [JsonPropertyName("transferred_value")] + public ulong TransferredValue { get; init; } + + const ushort TAG_FIELD_INDEX = 0; + const byte STORED_VARIANT = 1; + const ushort STORED_ID_INDEX = 1; + const ushort STORED_RUNTIME_INDEX = 2; + const ushort STORED_TRANSFERRED_VALUE_INDEX = 3; + public byte[] ToBytes() + { + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, new byte[] { STORED_VARIANT }) + .AddField(STORED_ID_INDEX, Id.ToBytes()) + .AddField(STORED_RUNTIME_INDEX, new byte[] { (byte)Runtime}) + .AddField(STORED_TRANSFERRED_VALUE_INDEX, CLValue.U64(TransferredValue)) + .GetBytes(); + } } public class SessionTransactionV1Target : ITransactionV1Target { + /// + /// Flag determining if the Wasm is an install/upgrade. + /// + [JsonPropertyName("is_install_upgrade")] + public bool IsInstallUpgrade { get; init; } + /// /// Wasm bytes for a Session transaction type. /// @@ -167,6 +262,40 @@ public class SessionTransactionV1Target : ITransactionV1Target /// [JsonPropertyName("runtime")] public TransactionRuntime Runtime { get; set; } + + /// + /// The amount of motes to transfer before code is executed. + /// This is for protection against phishing attack where a malicious session code drains + /// the balance of the caller account. The amount stated here is the maximum amount + /// that can be transferred from the caller account to the session account. + /// + [JsonPropertyName("transferred_value")] + public ulong TransferredValue { get; init; } + + /// + /// The seed for the session code that is used for an installer. + /// + [JsonPropertyName("seed")] + public string Seed { get; init; } + + const ushort TAG_FIELD_INDEX = 0; + const byte SESSION_VARIANT = 2; + const ushort SESSION_IS_INSTALL_INDEX = 1; + const ushort SESSION_RUNTIME_INDEX = 2; + const ushort SESSION_MODULE_BYTES_INDEX = 3; + const ushort SESSION_TRANSFERRED_VALUE_INDEX = 4; + const ushort SESSION_SEED_INDEX = 5; + public byte[] ToBytes() + { + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, new byte[] { SESSION_VARIANT }) + .AddField(SESSION_IS_INSTALL_INDEX, new byte[] { IsInstallUpgrade ? (byte)0x01 :(byte) 0x00 }) + .AddField(SESSION_RUNTIME_INDEX, new byte[] { (byte)Runtime }) + .AddField(SESSION_MODULE_BYTES_INDEX, ModuleBytes) + .AddField(SESSION_TRANSFERRED_VALUE_INDEX, CLValue.U64(TransferredValue)) + .AddField(SESSION_SEED_INDEX, Hex.Decode(Seed)) + .GetBytes(); + } } public class TransactionV1Target @@ -212,132 +341,5 @@ public static ITransactionV1Target Session(byte[] moduleBytes) ModuleBytes = moduleBytes, }; } - - public class TransactionTargetConverter : JsonConverter - { - public override ITransactionV1Target Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.String) - { - var targetType = reader.GetString(); - - switch (targetType) - { - case "Native": - return new NativeTransactionV1Target(); - default: - throw new JsonException($"TransactionTargetType '{targetType}' not supported."); - } - } - if (reader.TokenType == JsonTokenType.StartObject) - { - ITransactionV1Target transactionTarget = null; - IInvocationTarget id = null; - string module_bytes = null; - TransactionRuntime runtime = TransactionRuntime.VmCasperV1; - - reader.Read(); // skip start object - var targetType = reader.GetString(); - reader.Read(); - - switch (targetType) - { - case "Stored": - reader.Read(); - while (reader.TokenType != JsonTokenType.EndObject) - { - var prop = reader.GetString(); - reader.Read(); - switch (prop) - { - case "id": - id = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); - break; - case "runtime": - runtime = EnumCompat.Parse(reader.GetString()); - reader.Read(); // skip end object - break; - } - } - - reader.Read(); // skip end object - - transactionTarget = new StoredTransactionV1Target() - { - Id = id, - Runtime = runtime, - }; - break; - case "Session": - reader.Read(); - while (reader.TokenType != JsonTokenType.EndObject) - { - var prop = reader.GetString(); - reader.Read(); - switch (prop) - { - case "module_bytes": - module_bytes = reader.GetString(); - break; - case "runtime": - runtime = EnumCompat.Parse(reader.GetString()); - break; - } - } - - reader.Read(); // skip end object - - transactionTarget = new SessionTransactionV1Target() - { - ModuleBytes = Hex.Decode(module_bytes), - Runtime = runtime, - }; - break; - default: - throw new JsonException($"TransactionTargetType '{targetType}' not supported."); - } - - return transactionTarget; - } - - throw new JsonException("Cannot deserialize TransactionTarget. PropertyName expected"); - } - - public override void Write( - Utf8JsonWriter writer, - ITransactionV1Target value, - JsonSerializerOptions options) - { - switch (value) - { - case NativeTransactionV1Target: - writer.WriteStringValue("Native"); - break; - case StoredTransactionV1Target storedTarget: - writer.WriteStartObject(); - writer.WriteStartObject("Stored"); - writer.WritePropertyName("id"); - JsonSerializer.Serialize(writer, storedTarget.Id); - writer.WriteString("runtime", storedTarget.Runtime.ToString()); - writer.WriteEndObject(); - writer.WriteEndObject(); - break; - case SessionTransactionV1Target sessionTarget: - writer.WriteStartObject(); - writer.WriteStartObject("Session"); - writer.WriteString("module_bytes", Hex.ToHexString(sessionTarget.ModuleBytes)); - writer.WriteString("runtime", sessionTarget.Runtime.ToString()); - writer.WriteEndObject(); - writer.WriteEndObject(); - break; - default: - throw new JsonException("Cannot serialize empty transaction target."); - } - } - } } } \ No newline at end of file From 9c5ede54f9388feded83c01fd726874dd3d589b6 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 21 Nov 2024 11:57:12 +0100 Subject: [PATCH 085/126] bugfix: ContractWasm is a complex type, not a string Signed-off-by: David Hernando --- Casper.Network.SDK/Types/ContractWasm.cs | 13 +++++++++++++ Casper.Network.SDK/Types/StoredValue.cs | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 Casper.Network.SDK/Types/ContractWasm.cs diff --git a/Casper.Network.SDK/Types/ContractWasm.cs b/Casper.Network.SDK/Types/ContractWasm.cs new file mode 100644 index 0000000..f9ddb3d --- /dev/null +++ b/Casper.Network.SDK/Types/ContractWasm.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Types +{ + /// + /// A container for contract's WASM bytes. + /// + public class ContractWasm + { + [JsonPropertyName("bytes")] + public string Bytes { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/StoredValue.cs b/Casper.Network.SDK/Types/StoredValue.cs index 8b19662..eb360d4 100644 --- a/Casper.Network.SDK/Types/StoredValue.cs +++ b/Casper.Network.SDK/Types/StoredValue.cs @@ -18,7 +18,7 @@ public class StoredValue public Account Account { get; init; } - public string ContractWasm { get; init; } + public ContractWasm ContractWasm { get; init; } public ContractPackage ContractPackage { get; init; } From d815adeb25d3ab6ba8aaf1e9df5bd19bbf6e6e56 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 26 Nov 2024 17:10:37 +0100 Subject: [PATCH 086/126] TransactionV1 serialization Signed-off-by: David Hernando --- Casper.Network.SDK.Test/NctlContractTest.cs | 2 +- .../Converters/NamedArgsListConverter.cs | 90 +++++++++++ Casper.Network.SDK/Types/InitiatorAddr.cs | 2 +- Casper.Network.SDK/Types/PricingMode.cs | 14 +- Casper.Network.SDK/Types/TransactionV1.cs | 42 +++-- .../Types/TransactionV1Payload.cs | 144 +++++++----------- .../Types/TransactionV1Scheduling.cs | 82 +++++++++- .../Types/TransactionV1Target.cs | 127 +++++++++++++++ 8 files changed, 389 insertions(+), 114 deletions(-) create mode 100644 Casper.Network.SDK/Converters/NamedArgsListConverter.cs diff --git a/Casper.Network.SDK.Test/NctlContractTest.cs b/Casper.Network.SDK.Test/NctlContractTest.cs index dd0e033..1405f73 100644 --- a/Casper.Network.SDK.Test/NctlContractTest.cs +++ b/Casper.Network.SDK.Test/NctlContractTest.cs @@ -79,7 +79,7 @@ public async Task QueryContractKeysTest() var contractWasmKey = GlobalStateKey.FromString(contractInfo.ContractWasmHash); var rpcResponse4 = await _client.QueryGlobalState(contractWasmKey); var contractWasmInfo = rpcResponse4.Parse().StoredValue.ContractWasm; - Assert.IsFalse(string.IsNullOrWhiteSpace(contractWasmInfo)); + Assert.IsFalse(string.IsNullOrWhiteSpace(contractWasmInfo.Bytes)); } [Test, Order(4)] diff --git a/Casper.Network.SDK/Converters/NamedArgsListConverter.cs b/Casper.Network.SDK/Converters/NamedArgsListConverter.cs new file mode 100644 index 0000000..570cda5 --- /dev/null +++ b/Casper.Network.SDK/Converters/NamedArgsListConverter.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Casper.Network.SDK.Converters +{ + public class NamedArgsListConverter : JsonConverter> + { + public override List Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) + throw new JsonException("Array/Object start token expected to deserialize a list of NamedArgs"); + + reader.Read(); // Start object + + var named = reader.GetString(); + reader.Read(); + if (named != "Named") + throw new JsonException("Named property expected"); + + reader.Read(); // Start array + + List list = new List(); + + if (reader.TokenType == JsonTokenType.EndObject || + reader.TokenType == JsonTokenType.EndArray) + return list; //this is an empty list, just return... + + var tConverter = Activator.CreateInstance(typeof(TConverter)) as JsonConverter; + + if (reader.TokenType is JsonTokenType.StartArray or JsonTokenType.StartObject or JsonTokenType.PropertyName) + { + while (reader.TokenType == JsonTokenType.StartObject || + reader.TokenType == JsonTokenType.StartArray || + (tConverter is IDeserializeAsList && reader.TokenType == JsonTokenType.PropertyName)) + { + var element = JsonSerializer.Deserialize(ref reader, new JsonSerializerOptions() + { + Converters = { tConverter } + }); + reader.Read(); // end object/array + + list.Add(element); + } + } + else + { + while (reader.TokenType != JsonTokenType.EndArray) + { + var element = JsonSerializer.Deserialize(ref reader, new JsonSerializerOptions() + { + Converters = { tConverter } + }); + list.Add(element); + reader.Read(); + } + } + + reader.Read(); //read end object + return list; + } + + public override void Write( + Utf8JsonWriter writer, + List value, + JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WritePropertyName("Named"); + writer.WriteStartArray(); + var tConverter = Activator.CreateInstance(typeof(TConverter)) as JsonConverter; + var elOptions = new JsonSerializerOptions() + { + WriteIndented = options.WriteIndented, + Converters = { tConverter } + }; + foreach (var element in value) + { + JsonSerializer.Serialize(writer, element, elOptions); + } + + writer.WriteEndArray(); + writer.WriteEndObject(); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/InitiatorAddr.cs b/Casper.Network.SDK/Types/InitiatorAddr.cs index 63514e2..ecfbe37 100644 --- a/Casper.Network.SDK/Types/InitiatorAddr.cs +++ b/Casper.Network.SDK/Types/InitiatorAddr.cs @@ -80,7 +80,7 @@ public byte[] ToBytes() if(AccountHash != null) return new CalltableSerialization() .AddField(TAG_FIELD_INDEX, new byte[] { ACCOUNT_HASH_VARIANT_TAG }) - .AddField(PUBLIC_KEY_FIELD_INDEX, CLValue.Key(AccountHash)) + .AddField(PUBLIC_KEY_FIELD_INDEX, AccountHash.RawBytes) .GetBytes(); throw new Exception("Unable to serialize initiator addr"); diff --git a/Casper.Network.SDK/Types/PricingMode.cs b/Casper.Network.SDK/Types/PricingMode.cs index ffb6404..6726102 100644 --- a/Casper.Network.SDK/Types/PricingMode.cs +++ b/Casper.Network.SDK/Types/PricingMode.cs @@ -77,13 +77,6 @@ public byte[] ToBytes() public class FixedPricingMode : IPricingMode { - /// - /// User-specified gas_price tolerance (minimum 1). This is interpreted to mean "do not include this - /// transaction in a block if the current gas price is greater than this number". - /// - [JsonPropertyName("gas_price_tolerance")] - public byte GasPriceTolerance { get; init; } - /// /// User-specified additional computation factor (minimum 0). If "0" is provided, /// no additional logic is applied to the computation limit. Each value above "0" @@ -96,6 +89,13 @@ public class FixedPricingMode : IPricingMode [JsonPropertyName("additional_computation_factor")] public byte AdditionalComputationFactor { get; init; } + /// + /// User-specified gas_price tolerance (minimum 1). This is interpreted to mean "do not include this + /// transaction in a block if the current gas price is greater than this number". + /// + [JsonPropertyName("gas_price_tolerance")] + public byte GasPriceTolerance { get; init; } + const ushort TAG_FIELD_INDEX = 0; const byte FIXED_VARIANT_TAG = 1; const ushort FIXED_GAS_PRICE_TOLERANCE_INDEX = 1; diff --git a/Casper.Network.SDK/Types/TransactionV1.cs b/Casper.Network.SDK/Types/TransactionV1.cs index 9b57315..17269fe 100644 --- a/Casper.Network.SDK/Types/TransactionV1.cs +++ b/Casper.Network.SDK/Types/TransactionV1.cs @@ -21,18 +21,18 @@ public class TransactionV1 [JsonPropertyName("hash")] public string Hash { get; } - /// - /// List of signers and signatures for this transaction. - /// - [JsonPropertyName("approvals")] - public List Approvals { get; } = new List(); - /// /// Payload for this transaction. /// [JsonPropertyName("payload")] public TransactionV1Payload Payload { get; init; } + /// + /// List of signers and signatures for this transaction. + /// + [JsonPropertyName("approvals")] + public List Approvals { get; } = new List(); + /// /// Loads and deserializes a TransactionV1 from a file. /// @@ -173,20 +173,30 @@ public int GetTransactionSizeInBytes() return this.ToBytes().Length; } + const ushort HASH_FIELD_INDEX = 0; + const ushort PAYLOAD_FIELD_INDEX = 1; + const ushort APPROVALS_FIELD_INDEX = 2; + + public byte[] ToBytes() { + // add the approvals + // var ms = new MemoryStream(); - - ms.Write(Hex.Decode(this.Hash)); - - ms.Write(this.Payload.ToBytes()); - - var approvalSerializer = new DeployApprovalByteSerializer(); - ms.Write(LittleEndianConverter.GetBytes(this.Approvals.Count)); + var count = LittleEndianConverter.GetBytes(this.Approvals.Count); + ms.Write(count, 0, count.Length); foreach (var approval in this.Approvals) - ms.Write(approvalSerializer.ToBytes(approval)); - - return ms.ToArray(); + { + var approvalSerializer = new DeployApprovalByteSerializer(); + var approvalBytes = approvalSerializer.ToBytes(approval); + ms.Write(approvalBytes, 0, approvalBytes.Length); + } + + return new CalltableSerialization() + .AddField(HASH_FIELD_INDEX, Hex.Decode(this.Hash)) + .AddField(PAYLOAD_FIELD_INDEX, this.Payload.ToBytes()) + .AddField(APPROVALS_FIELD_INDEX, ms.ToArray()) + .GetBytes(); } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionV1Payload.cs b/Casper.Network.SDK/Types/TransactionV1Payload.cs index 8815224..41f466c 100644 --- a/Casper.Network.SDK/Types/TransactionV1Payload.cs +++ b/Casper.Network.SDK/Types/TransactionV1Payload.cs @@ -11,9 +11,24 @@ namespace Casper.Network.SDK.Types { - [JsonConverter(typeof(PayloadFieldsConverter))] public class PayloadFields : BaseByteSerializer { + [JsonPropertyName("args")] + [JsonConverter(typeof(NamedArgsListConverter))] + public List RuntimeArgs { get; init; } + + [JsonPropertyName("entry_point")] + [JsonConverter(typeof(TransactionV1EntryPoint.TransactionEntryPointConverter))] + public ITransactionV1EntryPoint EntryPoint { get; init; } + + [JsonPropertyName("scheduling")] + [JsonConverter(typeof(TransactionV1Scheduling.TransactionV1SchedulingConverter))] + public ITransactionV1Scheduling Scheduling { get; init; } + + [JsonPropertyName("target")] + [JsonConverter(typeof(TransactionV1Target.TransactionTargetConverter))] + public ITransactionV1Target Target { get; init; } + private readonly Dictionary _fields = new Dictionary(); public void AddField(ushort field, byte[] value) @@ -21,58 +36,6 @@ public void AddField(ushort field, byte[] value) _fields.Add(field, value); } - public class PayloadFieldsConverter : JsonConverter - { - public override PayloadFields Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - var payload = new PayloadFields(); - try - { - reader.Read(); // skip start object - while (reader.TokenType == JsonTokenType.PropertyName) - { - var fieldKey = reader.GetString(); - if (ushort.TryParse(fieldKey, out var field)) - { - reader.Read(); - var value = reader.GetString(); - payload._fields.Add(field, Hex.Decode(value)); - } - } - } - catch (Exception e) - { - throw new JsonException(e.Message); - } - - return payload; - } - - const ushort ARGS_MAP_KEY = 0; - const ushort TARGET_MAP_KEY = 1; - const ushort ENTRY_POINT_MAP_KEY = 2; - const ushort SCHEDULING_MAP_KEY = 3; - - public override void Write( - Utf8JsonWriter writer, - PayloadFields payload, - JsonSerializerOptions options) - { - writer.WriteStartObject(); - if(payload._fields.TryGetValue(ARGS_MAP_KEY, out var argsMapField)) - writer.WriteString(ARGS_MAP_KEY.ToString(), Hex.ToHexString(argsMapField)); - if(payload._fields.TryGetValue(TARGET_MAP_KEY, out var targetMapField)) - writer.WriteString(TARGET_MAP_KEY.ToString(), Hex.ToHexString(targetMapField)); - if(payload._fields.TryGetValue(ENTRY_POINT_MAP_KEY, out var entryPointMapField)) - writer.WriteString(ENTRY_POINT_MAP_KEY.ToString(), Hex.ToHexString(entryPointMapField)); - if(payload._fields.TryGetValue(SCHEDULING_MAP_KEY, out var schedulingMapField)) - writer.WriteString(SCHEDULING_MAP_KEY.ToString(), Hex.ToHexString(schedulingMapField)); - writer.WriteEndObject(); - } - } public byte[] ToBytes() { var fields_bytes = new MemoryStream(); @@ -87,7 +50,7 @@ public byte[] ToBytes() } } - internal class TransactionV1PayloadJson + public class TransactionV1PayloadJson { [JsonPropertyName("initiator_addr")] public InitiatorAddr InitiatorAddr { get; set; } @@ -104,27 +67,21 @@ internal class TransactionV1PayloadJson [JsonConverter(typeof(HumanizeTTLConverter))] public ulong Ttl { get; set; } + /// + /// Name of the chain where the deploy is executed. + /// + [JsonPropertyName("chain_name")] + public string ChainName { get; set; } + /// /// Pricing mode of a Transaction. /// [JsonPropertyName("pricing_mode")] [JsonConverter(typeof(PricingMode.PricingModeConverter))] public IPricingMode PricingMode { get; set; } - - /// - /// Name of the chain where the deploy is executed. - /// - [JsonPropertyName("chain_name")] - public string ChainName { get; set; } [JsonPropertyName("fields")] - [JsonConverter(typeof(PayloadFields.PayloadFieldsConverter))] public PayloadFields Fields { get; set; } - - public TransactionV1PayloadJson() - { - Fields = new PayloadFields(); - } } /// @@ -160,12 +117,6 @@ public class TransactionV1Payload [JsonConverter(typeof(PricingMode.PricingModeConverter))] public IPricingMode PricingMode { get; set; } - /// - /// Name of the chain where the deploy is executed. - /// - [JsonPropertyName("chain_name")] - public string ChainName { get; set; } - // [JsonPropertyName("fields")] // public PayloadFields Fields { get; set; } @@ -208,7 +159,7 @@ public override TransactionV1Payload Read( { try { - reader.Read(); // skip start object + // reader.Read(); // skip start object var payloadJson = JsonSerializer.Deserialize(ref reader, options); return new TransactionV1Payload() @@ -218,7 +169,11 @@ public override TransactionV1Payload Read( Ttl = payloadJson.Ttl, PricingMode = payloadJson.PricingMode, ChainName = payloadJson.ChainName, - }; //TODO: Deserialize fields!!! + RuntimeArgs = payloadJson.Fields.RuntimeArgs, + Target = payloadJson.Fields.Target, + EntryPoint = payloadJson.Fields.EntryPoint, + Scheduling = payloadJson.Fields.Scheduling, + }; } catch (Exception e) { @@ -238,16 +193,14 @@ public override void Write( Ttl = payload.Ttl, PricingMode = payload.PricingMode, ChainName = payload.ChainName, + Fields = new PayloadFields() + { + RuntimeArgs = payload.RuntimeArgs, + Target = payload.Target, + EntryPoint = payload.EntryPoint, + Scheduling = payload.Scheduling, + } }; - var ms = new MemoryStream(); - var namedArgSerializer = new NamedArgByteSerializer(); - ms.Write(BitConverter.GetBytes(payload.RuntimeArgs.Count)); - foreach (var args in payload.RuntimeArgs) - ms.Write(namedArgSerializer.ToBytes(args)); - payloadJson.Fields.AddField(0, ms.ToArray()); - payloadJson.Fields.AddField(1, payload.Target.ToBytes()); - payloadJson.Fields.AddField(2, payload.EntryPoint.ToBytes()); - payloadJson.Fields.AddField(3, payload.Scheduling.ToBytes()); JsonSerializer.Serialize(writer, payloadJson, options); } } @@ -268,15 +221,32 @@ public byte[] ToBytes() { var ms = new MemoryStream(); var namedArgSerializer = new NamedArgByteSerializer(); + ms.WriteByte(0x00); ms.Write(BitConverter.GetBytes(RuntimeArgs.Count)); foreach (var args in RuntimeArgs) ms.Write(namedArgSerializer.ToBytes(args)); + + var ms2 = new MemoryStream(); + ms2.Write(BitConverter.GetBytes(ms.ToArray().Length)); + ms2.Write(ms.ToArray()); var fields = new PayloadFields(); - fields.AddField(ARGS_MAP_KEY, ms.ToArray()); - fields.AddField(TARGET_MAP_KEY, Target.ToBytes()); - fields.AddField(ENTRY_POINT_MAP_KEY, EntryPoint.ToBytes()); - fields.AddField(SCHEDULING_MAP_KEY, Scheduling.ToBytes()); + fields.AddField(ARGS_MAP_KEY, ms2.ToArray()); + + ms2 = new MemoryStream(); + ms2.Write(BitConverter.GetBytes(Target.ToBytes().Length)); + ms2.Write(Target.ToBytes()); + fields.AddField(TARGET_MAP_KEY, ms2.ToArray()); + + ms2 = new MemoryStream(); + ms2.Write(BitConverter.GetBytes(EntryPoint.ToBytes().Length)); + ms2.Write(EntryPoint.ToBytes()); + fields.AddField(ENTRY_POINT_MAP_KEY, ms2.ToArray()); + + ms2 = new MemoryStream(); + ms2.Write(BitConverter.GetBytes(Scheduling.ToBytes().Length)); + ms2.Write(Scheduling.ToBytes()); + fields.AddField(SCHEDULING_MAP_KEY, ms2.ToArray()); return new CalltableSerialization() .AddField(INITIATOR_ADDR_FIELD_INDEX, InitiatorAddr.ToBytes()) diff --git a/Casper.Network.SDK/Types/TransactionV1Scheduling.cs b/Casper.Network.SDK/Types/TransactionV1Scheduling.cs index 4af7687..19eb2b0 100644 --- a/Casper.Network.SDK/Types/TransactionV1Scheduling.cs +++ b/Casper.Network.SDK/Types/TransactionV1Scheduling.cs @@ -109,7 +109,85 @@ public static ITransactionV1Scheduling FutureTimestamp(ulong timestamp) } public class TransactionV1Scheduling : TransactionScheduling - { - + { + public class TransactionV1SchedulingConverter : JsonConverter + { + public override ITransactionV1Scheduling Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + ITransactionV1Scheduling v1Scheduling; + + if (reader.TokenType == JsonTokenType.String) + { + var schedulingType = reader.GetString(); + switch (schedulingType) + { + case "Standard": + v1Scheduling = new StandardTransactionV1Scheduling(); + break; + default: + throw new JsonException( + "Cannot deserialize TransactionScheduling. Unknown scheduling type"); + } + } + else if (reader.TokenType == JsonTokenType.StartObject) + { + reader.Read(); // skip start object + var schedulingType = reader.GetString(); + reader.Read(); + switch (schedulingType) + { + case "FutureEra": + v1Scheduling = new FutureEraTransactionV1Scheduling + { + EraId = reader.GetUInt64(), + }; + break; + case "FutureTimestamp": + v1Scheduling = new FutureTimestampTransactionV1Scheduling + { + Timestamp = DateUtils.ToEpochTime(reader.GetString()), + }; + break; + default: + throw new JsonException( + "Cannot deserialize TransactionScheduling. Unknown scheduling type"); + } + + reader.Read(); + } + else + throw new JsonException("Cannot deserialize TransactionScheduling."); + + return v1Scheduling; + } + + public override void Write( + Utf8JsonWriter writer, + ITransactionV1Scheduling value, + JsonSerializerOptions options) + { + switch (value) + { + case StandardTransactionV1Scheduling: + writer.WriteStringValue("Standard"); + break; + case FutureEraTransactionV1Scheduling eraScheduling: + writer.WriteStartObject(); + writer.WriteNumber("FutureEra", eraScheduling.EraId); + writer.WriteEndObject(); + break; + case FutureTimestampTransactionV1Scheduling timestampScheduling: + writer.WriteStartObject(); + writer.WriteString("FutureTimestamp", DateUtils.ToISOString(timestampScheduling.Timestamp)); + writer.WriteEndObject(); + break; + default: + throw new JsonException("Cannot serialize due to unkown transaction scheduling type."); + } + } + } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionV1Target.cs b/Casper.Network.SDK/Types/TransactionV1Target.cs index f531536..be8e1e5 100644 --- a/Casper.Network.SDK/Types/TransactionV1Target.cs +++ b/Casper.Network.SDK/Types/TransactionV1Target.cs @@ -341,5 +341,132 @@ public static ITransactionV1Target Session(byte[] moduleBytes) ModuleBytes = moduleBytes, }; } + + public class TransactionTargetConverter : JsonConverter + { + public override ITransactionV1Target Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + var targetType = reader.GetString(); + + switch (targetType) + { + case "Native": + return new NativeTransactionV1Target(); + default: + throw new JsonException($"TransactionTargetType '{targetType}' not supported."); + } + } + if (reader.TokenType == JsonTokenType.StartObject) + { + ITransactionV1Target transactionTarget = null; + IInvocationTarget id = null; + string module_bytes = null; + TransactionRuntime runtime = TransactionRuntime.VmCasperV1; + + reader.Read(); // skip start object + var targetType = reader.GetString(); + reader.Read(); + + switch (targetType) + { + case "Stored": + reader.Read(); + while (reader.TokenType != JsonTokenType.EndObject) + { + var prop = reader.GetString(); + reader.Read(); + switch (prop) + { + case "id": + id = JsonSerializer.Deserialize(ref reader, options); + reader.Read(); + break; + case "runtime": + runtime = EnumCompat.Parse(reader.GetString()); + reader.Read(); // skip end object + break; + } + } + + reader.Read(); // skip end object + + transactionTarget = new StoredTransactionV1Target() + { + Id = id, + Runtime = runtime, + }; + break; + case "Session": + reader.Read(); + while (reader.TokenType != JsonTokenType.EndObject) + { + var prop = reader.GetString(); + reader.Read(); + switch (prop) + { + case "module_bytes": + module_bytes = reader.GetString(); + break; + case "runtime": + runtime = EnumCompat.Parse(reader.GetString()); + break; + } + } + + reader.Read(); // skip end object + + transactionTarget = new SessionTransactionV1Target() + { + ModuleBytes = Hex.Decode(module_bytes), + Runtime = runtime, + }; + break; + default: + throw new JsonException($"TransactionTargetType '{targetType}' not supported."); + } + + return transactionTarget; + } + + throw new JsonException("Cannot deserialize TransactionTarget. PropertyName expected"); + } + + public override void Write( + Utf8JsonWriter writer, + ITransactionV1Target value, + JsonSerializerOptions options) + { + switch (value) + { + case NativeTransactionV1Target: + writer.WriteStringValue("Native"); + break; + case StoredTransactionV1Target storedTarget: + writer.WriteStartObject(); + writer.WriteStartObject("Stored"); + writer.WritePropertyName("id"); + JsonSerializer.Serialize(writer, storedTarget.Id); + writer.WriteString("runtime", storedTarget.Runtime.ToString()); + writer.WriteEndObject(); + writer.WriteEndObject(); + break; + case SessionTransactionV1Target sessionTarget: + writer.WriteStartObject(); + writer.WriteStartObject("Session"); + writer.WriteString("module_bytes", Hex.ToHexString(sessionTarget.ModuleBytes)); + writer.WriteString("runtime", sessionTarget.Runtime.ToString()); + writer.WriteEndObject(); + writer.WriteEndObject(); + break; + default: + throw new JsonException("Cannot serialize empty transaction target."); + } + } + } } } \ No newline at end of file From 13284c58d571955391503fea28090bf0a005b5a5 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 26 Nov 2024 17:12:30 +0100 Subject: [PATCH 087/126] TransactionV1 serialization Signed-off-by: David Hernando --- Casper.Network.SDK/Types/TransactionV1Payload.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Casper.Network.SDK/Types/TransactionV1Payload.cs b/Casper.Network.SDK/Types/TransactionV1Payload.cs index 41f466c..3b1dd67 100644 --- a/Casper.Network.SDK/Types/TransactionV1Payload.cs +++ b/Casper.Network.SDK/Types/TransactionV1Payload.cs @@ -110,6 +110,12 @@ public class TransactionV1Payload [JsonConverter(typeof(HumanizeTTLConverter))] public ulong Ttl { get; set; } + /// + /// Name of the chain where the deploy is executed. + /// + [JsonPropertyName("chain_name")] + public string ChainName { get; set; } + /// /// Pricing mode of a Transaction. /// From 59a78f5c2463c4fc55137b511a2c7ff709313f4c Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 21 Nov 2024 10:55:39 +0100 Subject: [PATCH 088/126] Changes to StoredValue type for rc5 Signed-off-by: David Hernando --- Casper.Network.SDK/Types/BidKind.cs | 34 ++++++++++++++++++- .../Types/{Reservation.cs => Prepayment.cs} | 14 ++++---- Casper.Network.SDK/Types/StoredValue.cs | 9 +++-- Casper.Network.SDK/Types/ValidatorBid.cs | 6 ++++ 4 files changed, 53 insertions(+), 10 deletions(-) rename Casper.Network.SDK/Types/{Reservation.cs => Prepayment.cs} (53%) diff --git a/Casper.Network.SDK/Types/BidKind.cs b/Casper.Network.SDK/Types/BidKind.cs index e88fa70..5491767 100644 --- a/Casper.Network.SDK/Types/BidKind.cs +++ b/Casper.Network.SDK/Types/BidKind.cs @@ -9,7 +9,6 @@ namespace Casper.Network.SDK.Types /// public class Bridge { - /// /// Previous validator public key associated with the bid. /// @@ -56,6 +55,33 @@ public class ValidatorCredit [JsonConverter(typeof(PublicKey.PublicKeyConverter))] public PublicKey Validator { get; init; } } + + /// + /// Represents a validator reserving a slot for specific delegator" + /// + public class Reservation + { + /// + /// The validator public key. + /// + [JsonPropertyName("validator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey ValidatorPublicKey { get; init; } + + /// + /// The delegator public key. + /// + [JsonPropertyName("delegator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey DelegatorPublicKey { get; init; } + + /// + /// The delegation rate. + /// + /// + [JsonPropertyName("delegation_rate")] + public uint DelegationRate { get; init; } + } /// /// Auction bid variants. @@ -93,5 +119,11 @@ public class BidKind /// [JsonPropertyName("Credit")] public ValidatorCredit Credit { get; init; } + + /// + /// Represents a validator reserving a slot for specific delegator" + /// + [JsonPropertyName("Reservation")] + public Reservation Reservation { get; init; } } } diff --git a/Casper.Network.SDK/Types/Reservation.cs b/Casper.Network.SDK/Types/Prepayment.cs similarity index 53% rename from Casper.Network.SDK/Types/Reservation.cs rename to Casper.Network.SDK/Types/Prepayment.cs index 37333e4..d009802 100644 --- a/Casper.Network.SDK/Types/Reservation.cs +++ b/Casper.Network.SDK/Types/Prepayment.cs @@ -3,17 +3,17 @@ namespace Casper.Network.SDK.Types { /// - /// Container for bytes recording location, type and data for a gas reservation. + /// Container for bytes recording location, type and data for a gas pre-payment. /// - public class Reservation + public class Prepayment { [JsonPropertyName("receipt")] public string Receipt { get; init; } - [JsonPropertyName("reservation_kind")] - public byte ReservationKind { get; init; } + [JsonPropertyName("prepayment_kind")] + public byte PrepaymentKind { get; init; } - [JsonPropertyName("reservation_data")] - public string ReservationData { get; init; } + [JsonPropertyName("prepayment_data")] + public string PrepaymentData { get; init; } } -} \ No newline at end of file +} diff --git a/Casper.Network.SDK/Types/StoredValue.cs b/Casper.Network.SDK/Types/StoredValue.cs index eb360d4..1425b19 100644 --- a/Casper.Network.SDK/Types/StoredValue.cs +++ b/Casper.Network.SDK/Types/StoredValue.cs @@ -67,12 +67,17 @@ public class StoredValue public NamedKeyValue NamedKey { get; init; } /// - /// Stores location, type and data for a gas reservation. + /// Stores location, type and data for a gas pre-payment. /// - public Reservation Reservation { get; init; } + public Prepayment Prepaid { get; init; } public EntryPoint EntryPoint { get; init; } + /// + /// Raw bytes. Similar to a [`crate::StoredValue::CLValue`] but does not incur overhead of a [`crate::CLValue`] and [`crate::CLType`]. + /// + public byte[] RawBytes { get; init; } + public class StoredValueConverter : JsonConverter { public override StoredValue Read diff --git a/Casper.Network.SDK/Types/ValidatorBid.cs b/Casper.Network.SDK/Types/ValidatorBid.cs index e0e9bfb..34e3b51 100644 --- a/Casper.Network.SDK/Types/ValidatorBid.cs +++ b/Casper.Network.SDK/Types/ValidatorBid.cs @@ -53,5 +53,11 @@ public class ValidatorBid /// [JsonPropertyName("maximum_delegation_amount")] public ulong MaximumDelegationAmount { get; init; } + + /// + /// Number of slots reserved for specific delegators + /// + [JsonPropertyName("reserved_slots")] + public uint ReservedSlots { get; init; } } } \ No newline at end of file From f21536e95a3106d310f4b68f6b2a4ce61689838a Mon Sep 17 00:00:00 2001 From: David Hernando Date: Sun, 17 Nov 2024 16:01:13 +0100 Subject: [PATCH 089/126] CSDK-1291 implemented new RPC method state_get_package. Signed-off-by: David Hernando --- .../RPCResponses/GetPackageResultTest.cs | 46 ++++++++++ ...-package-result-contract-package-v200.json | 19 +++++ .../get-package-result-package-v200.json | 20 +++++ Casper.Network.SDK/Casper.Network.SDK.csproj | 2 +- Casper.Network.SDK/ICasperClient.cs | 2 + Casper.Network.SDK/JsonRpc/CasperMethods.cs | 14 ++++ .../JsonRpc/ResultTypes/GetPackageResult.cs | 83 +++++++++++++++++++ Casper.Network.SDK/NetCasperClient.cs | 30 ++++++- Casper.Network.SDK/Types/Package.cs | 47 +++++++---- Casper.Network.SDK/Types/PackageIdentifier.cs | 43 ++++++++++ 10 files changed, 288 insertions(+), 18 deletions(-) create mode 100644 Casper.Network.SDK.Test/RPCResponses/GetPackageResultTest.cs create mode 100644 Casper.Network.SDK.Test/TestData/get-package-result-contract-package-v200.json create mode 100644 Casper.Network.SDK.Test/TestData/get-package-result-package-v200.json create mode 100644 Casper.Network.SDK/JsonRpc/ResultTypes/GetPackageResult.cs create mode 100644 Casper.Network.SDK/Types/PackageIdentifier.cs diff --git a/Casper.Network.SDK.Test/RPCResponses/GetPackageResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetPackageResultTest.cs new file mode 100644 index 0000000..2833370 --- /dev/null +++ b/Casper.Network.SDK.Test/RPCResponses/GetPackageResultTest.cs @@ -0,0 +1,46 @@ +using System.IO; +using Casper.Network.SDK.JsonRpc.ResultTypes; +using Casper.Network.SDK.Types; +using NUnit.Framework; + +namespace NetCasperTest.RPCResponses +{ + public class GetPackageResultTest + { + [Test] + public void GetPackageResultContractPackageTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-package-result-contract-package-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + Assert.IsNotNull(result.ContractPackage); + Assert.IsNull(result.Package); + Assert.AreEqual("uref-6fc684fea74b278cbb18b546a6d9242b810ce58a2ff05d17493b19aa08f540e0-007", result.ContractPackage.AccessKey.ToString()); + Assert.AreEqual(1, result.ContractPackage.Versions.Count); + Assert.AreEqual(2, result.ContractPackage.Versions[0].ProtocolVersionMajor); + Assert.AreEqual(1, result.ContractPackage.Versions[0].Version); + Assert.AreEqual("contract-25aa2d3cc62a302746c08ae885454d6e8a9c8609aaa7468b24284e5d29c5d2f1", result.ContractPackage.Versions[0].Hash); + Assert.AreEqual(LockStatus.Unlocked, result.ContractPackage.LockStatus); + } + [Test] + public void GetPackageResultPackageTest_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/get-package-result-package-v200.json"); + + var result = RpcResult.Parse(json); + Assert.IsNotNull(result); + Assert.AreEqual("2.0.0", result.ApiVersion); + Assert.IsNull(result.ContractPackage); + Assert.IsNotNull(result.Package); + Assert.AreEqual(1, result.Package.Versions.Count); + Assert.AreEqual(2, result.Package.Versions[0].EntityVersion.ProtocolVersionMajor); + Assert.AreEqual(1, result.Package.Versions[0].EntityVersion.Version); + Assert.AreEqual("addressable-entity-e51af99d88fd26a282de00271c49a6c256232b344aa7907d2c8603b2bd5217c9", result.Package.Versions[0].AddressableEntityHash); + Assert.AreEqual(LockStatus.Unlocked, result.Package.LockStatus); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/get-package-result-contract-package-v200.json b/Casper.Network.SDK.Test/TestData/get-package-result-contract-package-v200.json new file mode 100644 index 0000000..2a62f13 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-package-result-contract-package-v200.json @@ -0,0 +1,19 @@ +{ + "api_version": "2.0.0", + "package": { + "ContractPackage": { + "access_key": "uref-6fc684fea74b278cbb18b546a6d9242b810ce58a2ff05d17493b19aa08f540e0-007", + "versions": [ + { + "protocol_version_major": 2, + "contract_version": 1, + "contract_hash": "contract-25aa2d3cc62a302746c08ae885454d6e8a9c8609aaa7468b24284e5d29c5d2f1" + } + ], + "disabled_versions": [], + "groups": [], + "lock_status": "Unlocked" + } + }, + "merkle_proof": "010000000116aa0d0ad73d3534ba543a574f2a521fd770e4cb436ca0741e8a02eae8fdbf08046fc684fea74b278cbb18b546a6d9242b810ce58a2ff05d17493b19aa08f540e00701000000020000000100000025aa2d3cc62a302746c08ae885454d6e8a9c8609aaa7468b24284e5d29c5d2f1000000000000000000020000000016080000002500309ea1908b14a258eb4246b0b4cf2181f9a079e97571bb5cf5edf2cc6b1dd26848001b5a6e381c3fd5e9983555281b928e75ee6e6b17eb4ca779581b2073313e9e7d5300162d2614d188e62d6b8d8148b9455feedb784c39230ef2df6610f4706da05a466300d25c1b5580816475df74470a0c397baf8c4c5ea1016f9742414fc145d048aca3a600f91a5d4a604d79b244b83c89716caf516e75e951f4e32995101916fdeea30721ae00a77ba4e92715b5285ff5bbda6cfc1a75fb6160d9f8c237abe9c7eef3aa0d448abb0032729b6868c15a80e2315f291020073922c81a445f3220e6214262823e7690a7e3000264f63a177c0d3bdf4501f85fc99ef7a25335f15cf01c7e5092e101eb13c8810001090000000001a3595f9802a8c4ecc4f74cbbcf6ea4cd484fb643ee6ed49f6d6a8ad4816f123002013ba7feb38dc3c94f0fc6dd8eefa88b87e0a6d0fb6372d8c166c0861812efa0970601fbbff6ca6374c82315ce70bd07ad0b6e503aab81fb9eedf4eee2806cdb44f5310a0062e02bef4f516c7bf650d0e7a7f92c3278515b193eb2067cea0b40f8208626d40b00b7aac0f3f6692862b38c63e0df2a381945f48c00f7dba20994dde801b102e3eb0d004cc57d085abe7a34c1c509178c52296cedf995770f0023a195cbf3b713494a9c0e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f01951506a8635a859363abd1c96f88d1b972d2b8be5b8620c9e01d9cf0933a477b1500cb808b7d2846894f16ea158ab0df1e92b8ce3bf9e19d1e06e66662f424906cb9" +} diff --git a/Casper.Network.SDK.Test/TestData/get-package-result-package-v200.json b/Casper.Network.SDK.Test/TestData/get-package-result-package-v200.json new file mode 100644 index 0000000..20776d6 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/get-package-result-package-v200.json @@ -0,0 +1,20 @@ +{ + "api_version": "2.0.0", + "package": { + "Package": { + "versions": [ + { + "entity_version_key": { + "protocol_version_major": 2, + "entity_version": 1 + }, + "addressable_entity_hash": "addressable-entity-e51af99d88fd26a282de00271c49a6c256232b344aa7907d2c8603b2bd5217c9" + } + ], + "disabled_versions": [], + "groups": [], + "lock_status": "Unlocked" + } + }, + "merkle_proof": "010000000116aa0d0ad73d3534ba543a574f2a521fd770e4cb436ca0741e8a02eae8fdbf08046fc684fea74b278cbb18b546a6d9242b810ce58a2ff05d17493b19aa08f540e00701000000020000000100000025aa2d3cc62a302746c08ae885454d6e8a9c8609aaa7468b24284e5d29c5d2f1000000000000000000020000000016080000002500309ea1908b14a258eb4246b0b4cf2181f9a079e97571bb5cf5edf2cc6b1dd26848001b5a6e381c3fd5e9983555281b928e75ee6e6b17eb4ca779581b2073313e9e7d5300162d2614d188e62d6b8d8148b9455feedb784c39230ef2df6610f4706da05a466300d25c1b5580816475df74470a0c397baf8c4c5ea1016f9742414fc145d048aca3a600f91a5d4a604d79b244b83c89716caf516e75e951f4e32995101916fdeea30721ae00a77ba4e92715b5285ff5bbda6cfc1a75fb6160d9f8c237abe9c7eef3aa0d448abb0032729b6868c15a80e2315f291020073922c81a445f3220e6214262823e7690a7e3000264f63a177c0d3bdf4501f85fc99ef7a25335f15cf01c7e5092e101eb13c8810001090000000001a3595f9802a8c4ecc4f74cbbcf6ea4cd484fb643ee6ed49f6d6a8ad4816f123002013ba7feb38dc3c94f0fc6dd8eefa88b87e0a6d0fb6372d8c166c0861812efa0970601fbbff6ca6374c82315ce70bd07ad0b6e503aab81fb9eedf4eee2806cdb44f5310a0062e02bef4f516c7bf650d0e7a7f92c3278515b193eb2067cea0b40f8208626d40b00b7aac0f3f6692862b38c63e0df2a381945f48c00f7dba20994dde801b102e3eb0d004cc57d085abe7a34c1c509178c52296cedf995770f0023a195cbf3b713494a9c0e0043d39741a6e7b395eace3b1fbbbd5bbf288f7a788e80745a2e2bb0f2173a8b4f0f01951506a8635a859363abd1c96f88d1b972d2b8be5b8620c9e01d9cf0933a477b1500cb808b7d2846894f16ea158ab0df1e92b8ce3bf9e19d1e06e66662f424906cb9" +} diff --git a/Casper.Network.SDK/Casper.Network.SDK.csproj b/Casper.Network.SDK/Casper.Network.SDK.csproj index 4d3e459..d7f9ce7 100644 --- a/Casper.Network.SDK/Casper.Network.SDK.csproj +++ b/Casper.Network.SDK/Casper.Network.SDK.csproj @@ -26,7 +26,7 @@ - + diff --git a/Casper.Network.SDK/ICasperClient.cs b/Casper.Network.SDK/ICasperClient.cs index 6615575..68ee6db 100644 --- a/Casper.Network.SDK/ICasperClient.cs +++ b/Casper.Network.SDK/ICasperClient.cs @@ -40,6 +40,8 @@ public interface ICasperClient Task> GetEntity(string entityAddr, ulong blockHeight); + Task> GetPackage(string packageHash, string blockHash = null); + Task> QueryGlobalState(string key, ulong height, string path = null); Task> QueryGlobalState(string key, string stateRootHash = null, diff --git a/Casper.Network.SDK/JsonRpc/CasperMethods.cs b/Casper.Network.SDK/JsonRpc/CasperMethods.cs index 21ede29..de96560 100644 --- a/Casper.Network.SDK/JsonRpc/CasperMethods.cs +++ b/Casper.Network.SDK/JsonRpc/CasperMethods.cs @@ -156,6 +156,20 @@ public GetEntity(string addressableEntity, IBlockIdentifier blockIdentifier = nu { } } + + public class GetPackage : RpcMethod + { + public GetPackage(IPackageIdentifier packageIdentifier, IBlockIdentifier blockIdentifier = null) : base("state_get_package") + { + this.Parameters = new Dictionary + { + { "package_identifier", packageIdentifier.GetPackageIdentifier() } + }; + + if(blockIdentifier != null) + this.Parameters.Add("block_identifier", blockIdentifier.GetBlockIdentifier()); + } + } public class GetItem : RpcMethod { diff --git a/Casper.Network.SDK/JsonRpc/ResultTypes/GetPackageResult.cs b/Casper.Network.SDK/JsonRpc/ResultTypes/GetPackageResult.cs new file mode 100644 index 0000000..9043a30 --- /dev/null +++ b/Casper.Network.SDK/JsonRpc/ResultTypes/GetPackageResult.cs @@ -0,0 +1,83 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Types; + +namespace Casper.Network.SDK.JsonRpc.ResultTypes +{ + internal class PackageCompat : RpcResult + { + [JsonPropertyName("Package")] + public Package Package { get; init; } + + [JsonPropertyName("ContractPackage")] + public ContractPackage ContractPackage { get; init; } + } + + internal class GetPackageResultCompat : RpcResult + { + [JsonPropertyName("package")] + public PackageCompat Package { get; init; } + + [JsonPropertyName("merkle_proof")] + public string MerkleProof { get; init; } + } + + [JsonConverter(typeof(GetPackageResultConverter))] + public class GetPackageResult : RpcResult + { + /// + /// A legacy Contract Package. + /// + public ContractPackage ContractPackage { get; init; } + + /// + /// An Addressable Entity package. + /// + public Package Package { get; init; } + + /// + /// The merkle proof. + /// + public string MerkleProof { get; init; } + + public class GetPackageResultConverter : JsonConverter + { + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert == typeof(GetPackageResult); + } + + public override GetPackageResult Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options) + { + try + { + var resultCompat = JsonSerializer.Deserialize(ref reader, options); + + return new GetPackageResult() + { + ApiVersion = resultCompat.ApiVersion, + ContractPackage = resultCompat.Package.ContractPackage, + Package = resultCompat.Package.Package, + MerkleProof = resultCompat.MerkleProof, + }; + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + } + + public override void Write( + Utf8JsonWriter writer, + GetPackageResult block, + JsonSerializerOptions options) + { + throw new JsonException($"not implemented"); + } + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index 83fb2a2..00bfb38 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -221,7 +221,35 @@ public async Task> GetEntity(string entityAddr, ulo var method = new GetEntity(entityAddr, new BlockIdentifier(blockHeight)); return await SendRpcRequestAsync(method); } - + + /// + /// Returns a Package from the network + /// + /// The entity address to get information of. + /// A block hash for which the information of the entity is queried. Null for most recent information. + public async Task> GetPackage(string packageHash, string blockHash = null) + { + var method = new GetPackage(packageHash.StartsWith("package-") + ? PackageIdentifier.FromPackageAddr(packageHash) + : PackageIdentifier.FromContractPackageHash(packageHash), + blockHash != null ? new BlockIdentifier(blockHash) : null); + return await SendRpcRequestAsync(method); + } + + /// + /// Returns a Package from the network + /// + /// The package address or contract package hash to get information of. + /// A block height for which the information of the package is queried. + public async Task> GetPackage(string packageHash, ulong blockHeight) + { + var method = new GetPackage(packageHash.StartsWith("package-") + ? PackageIdentifier.FromPackageAddr(packageHash) + : PackageIdentifier.FromContractPackageHash(packageHash), + new BlockIdentifier(blockHeight)); + return await SendRpcRequestAsync(method); + } + /// /// Request a stored value from the network. This RPC is deprecated, use `QueryGlobalState` instead. /// diff --git a/Casper.Network.SDK/Types/Package.cs b/Casper.Network.SDK/Types/Package.cs index faaf973..6ef0efa 100644 --- a/Casper.Network.SDK/Types/Package.cs +++ b/Casper.Network.SDK/Types/Package.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; @@ -14,7 +15,7 @@ public class EntityVersion /// [JsonPropertyName("entity_version")] public uint Version { get; init; } - + /// /// Major element of `ProtocolVersion` a `ContractVersion` is compatible with. /// @@ -28,12 +29,13 @@ public enum PackageStatus /// The package is locked and cannot be versioned. /// Locked, + /// /// The package is unlocked and can be versioned. /// Unlocked, } - + public class EntityVersionAndHash { /// @@ -41,7 +43,7 @@ public class EntityVersionAndHash /// [JsonPropertyName("entity_version_key")] public EntityVersion EntityVersion { get; init; } - + /// /// The hex-encoded address of the addressable entity. /// @@ -56,7 +58,7 @@ public class NamedUserGroup /// [JsonPropertyName("group_name")] public string Name { get; init; } - + /// /// List of URefs associated with the group. /// @@ -64,31 +66,24 @@ public class NamedUserGroup [JsonConverter(typeof(GenericListConverter))] public List Users { get; init; } } - + /// /// Entity definition, metadata, and security container. /// public class Package { - /// - /// Key used to add or disable versions. - /// - [JsonPropertyName("access_key")] - [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] - public URef AccessKey { get; init; } - /// /// All versions (enabled and disabled). /// [JsonPropertyName("versions")] public List Versions { get; init; } - + /// /// Collection of disabled entity versions. The runtime will not permit disabled entity versions to be executed. /// [JsonPropertyName("disabled_versions")] public List DisabledVersions { get; init; } - + /// /// Mapping maintaining the set of URefs associated with each "user group". This can be used /// to control access to methods in a particular version of the entity. A method is callable @@ -96,12 +91,32 @@ public class Package /// [JsonPropertyName("groups")] public List Groups { get; init; } - + /// /// The current state of the contract package. /// - [JsonPropertyName("lock_status")] + [JsonPropertyName("lock_status")] [JsonConverter(typeof(JsonStringEnumConverter))] public LockStatus LockStatus { get; init; } + + public static Package FromContractPackage(ContractPackage contractPackage) + { + return new Package() + { + Versions = contractPackage.Versions.Select(v => new EntityVersionAndHash() + { + EntityVersion = new EntityVersion() + { Version = v.Version, ProtocolVersionMajor = v.ProtocolVersionMajor }, + AddressableEntityHash = v.Hash, + }).ToList(), + Groups = contractPackage.Groups.Select(g => new NamedUserGroup() + { + Name = g.Label, + Users = g.Keys + }) + .ToList(), + LockStatus = contractPackage.LockStatus, + }; + } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/PackageIdentifier.cs b/Casper.Network.SDK/Types/PackageIdentifier.cs new file mode 100644 index 0000000..6abe844 --- /dev/null +++ b/Casper.Network.SDK/Types/PackageIdentifier.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; + +namespace Casper.Network.SDK.Types +{ + /// + /// Identifier of a package by its contract package hash or its addressable entity package address. + /// + public interface IPackageIdentifier + { + public Dictionary GetPackageIdentifier(); + } + + /// + /// Identifier of a package by its contract package hash or its addressable entity package address. + /// + public class PackageIdentifier : IPackageIdentifier + { + private readonly string? _contractPackageHash; + private readonly string? _packageAddr; + + private PackageIdentifier(string? contractPackageHash = null, string? packageAddr = null) + { + _contractPackageHash = contractPackageHash; + _packageAddr = packageAddr; + } + + public static PackageIdentifier FromContractPackageHash(string contractPackageHash) => + new(contractPackageHash: contractPackageHash); + + public static PackageIdentifier FromPackageAddr(string packageAddr) => + new(packageAddr: packageAddr); + + /// + /// Returns a PackageIdentifier object as defined in the RPC schema. + /// + public Dictionary GetPackageIdentifier() + { + return !string.IsNullOrWhiteSpace(_contractPackageHash) + ? new Dictionary { { "ContractPackageHash", _contractPackageHash! } } + : new Dictionary { { "PackageAddr", _packageAddr! } }; + } + } +} \ No newline at end of file From 43560bb44fa0bc7f3042a965b02c2344e83b5e00 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 26 Nov 2024 17:12:30 +0100 Subject: [PATCH 090/126] TransactionV1 serialization Signed-off-by: David Hernando --- Casper.Network.SDK/Types/TransactionV1Payload.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Casper.Network.SDK/Types/TransactionV1Payload.cs b/Casper.Network.SDK/Types/TransactionV1Payload.cs index 41f466c..3b1dd67 100644 --- a/Casper.Network.SDK/Types/TransactionV1Payload.cs +++ b/Casper.Network.SDK/Types/TransactionV1Payload.cs @@ -110,6 +110,12 @@ public class TransactionV1Payload [JsonConverter(typeof(HumanizeTTLConverter))] public ulong Ttl { get; set; } + /// + /// Name of the chain where the deploy is executed. + /// + [JsonPropertyName("chain_name")] + public string ChainName { get; set; } + /// /// Pricing mode of a Transaction. /// From 84aabca9c8ef322360b8b9d697da7ca719856f3d Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 5 Dec 2024 18:07:13 +0100 Subject: [PATCH 091/126] WIP Signed-off-by: David Hernando --- .../ByteSerializers/BaseByteSerializer.cs | 1 + Casper.Network.SDK/Types/BidKind.cs | 29 +- Casper.Network.SDK/Types/Block.cs | 2 +- Casper.Network.SDK/Types/DelegatorBid.cs | 36 ++ Casper.Network.SDK/Types/DelegatorKind.cs | 49 ++ .../Types/GlobalStateKey/BidAddrKey.cs | 72 ++- Casper.Network.SDK/Types/Reservation.cs | 46 ++ .../Types/SeigniorageAllocation.cs | 14 +- .../Types/TransactionBuilder.cs | 450 ++++++++++++++---- .../Types/TransactionV1EntryPoint.cs | 6 + .../Types/TransactionV1Target.cs | 50 +- Docs/Examples/ListRewards/Program.cs | 2 +- 12 files changed, 608 insertions(+), 149 deletions(-) create mode 100644 Casper.Network.SDK/Types/DelegatorBid.cs create mode 100644 Casper.Network.SDK/Types/DelegatorKind.cs create mode 100644 Casper.Network.SDK/Types/Reservation.cs diff --git a/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs b/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs index 037ec0a..c7be481 100644 --- a/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs +++ b/Casper.Network.SDK/ByteSerializers/BaseByteSerializer.cs @@ -43,6 +43,7 @@ protected static void WriteBytes(MemoryStream ms, byte[] value) ms.Write(value); } + protected static void WriteString(MemoryStream ms, string value) { var valueBytes = System.Text.Encoding.UTF8.GetBytes(value); diff --git a/Casper.Network.SDK/Types/BidKind.cs b/Casper.Network.SDK/Types/BidKind.cs index 5491767..cc9cba7 100644 --- a/Casper.Network.SDK/Types/BidKind.cs +++ b/Casper.Network.SDK/Types/BidKind.cs @@ -55,33 +55,6 @@ public class ValidatorCredit [JsonConverter(typeof(PublicKey.PublicKeyConverter))] public PublicKey Validator { get; init; } } - - /// - /// Represents a validator reserving a slot for specific delegator" - /// - public class Reservation - { - /// - /// The validator public key. - /// - [JsonPropertyName("validator_public_key")] - [JsonConverter(typeof(PublicKey.PublicKeyConverter))] - public PublicKey ValidatorPublicKey { get; init; } - - /// - /// The delegator public key. - /// - [JsonPropertyName("delegator_public_key")] - [JsonConverter(typeof(PublicKey.PublicKeyConverter))] - public PublicKey DelegatorPublicKey { get; init; } - - /// - /// The delegation rate. - /// - /// - [JsonPropertyName("delegation_rate")] - public uint DelegationRate { get; init; } - } /// /// Auction bid variants. @@ -106,7 +79,7 @@ public class BidKind /// A bid record containing only delegator data. /// [JsonPropertyName("Delegator")] - public Delegator Delegator { get; init; } + public DelegatorBid Delegator { get; init; } /// /// A bridge record pointing to a new `ValidatorBid` after the public key was changed. diff --git a/Casper.Network.SDK/Types/Block.cs b/Casper.Network.SDK/Types/Block.cs index 1108044..b880043 100644 --- a/Casper.Network.SDK/Types/Block.cs +++ b/Casper.Network.SDK/Types/Block.cs @@ -515,7 +515,7 @@ public enum TransactionCategory { /// Transfer from a Deploy transaction. /// [Obsolete("Use Mint instead of DeployTransfer")] - DeployTransfer = 1, + DeployTransfer = 0, /// /// Native auction interaction. /// diff --git a/Casper.Network.SDK/Types/DelegatorBid.cs b/Casper.Network.SDK/Types/DelegatorBid.cs new file mode 100644 index 0000000..91ef6ea --- /dev/null +++ b/Casper.Network.SDK/Types/DelegatorBid.cs @@ -0,0 +1,36 @@ +using System.Numerics; +using System.Text.Json.Serialization; +using Casper.Network.SDK.Converters; + +namespace Casper.Network.SDK.Types +{ + /// + /// Represents a party delegating their stake to a validator (or \"delegatee + /// + public class DelegatorBid + { + /// + /// A Public key or Purse. Origin of the delegation. + /// + [JsonPropertyName("delegator_kind")] + public DelegatorKind DelegatorKind { get; init; } + + [JsonPropertyName("validator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey ValidatorPublicKey { get; init; } + + /// + /// Amount of Casper token (in motes) delegated + /// + [JsonPropertyName("staked_amount")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger StakedAmount { get; init; } + + [JsonPropertyName("bonding_purse")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public URef BondingPurse { get; init; } + + [JsonPropertyName("vesting_schedule")] + public VestingSchedule VestingSchedule { get; init; } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/DelegatorKind.cs b/Casper.Network.SDK/Types/DelegatorKind.cs new file mode 100644 index 0000000..50b380e --- /dev/null +++ b/Casper.Network.SDK/Types/DelegatorKind.cs @@ -0,0 +1,49 @@ + +using System.Collections.Generic; +using System.IO; +using System.Text.Json.Serialization; +using Org.BouncyCastle.Utilities.Encoders; + +namespace Casper.Network.SDK.Types +{ + /// + /// Auction bid variants. Kinds of delegation bids. + /// + public class DelegatorKind + { + /// + /// Delegation from public key. + /// + [JsonPropertyName("PublicKey")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey PublicKey { get; init; } + + /// + /// Delegation from purse. + /// + [JsonPropertyName("Purse")] + public string Purse { get; init; } + + public byte[] ToBytes() + { + var ms = new MemoryStream(); + if (PublicKey != null) + { + ms.WriteByte(0x00); + ms.Write(this.PublicKey.GetBytes()); + return ms.ToArray(); + } + else // Purse + { + ms.WriteByte(0x01); + ms.Write(Hex.Decode(Purse)); + return ms.ToArray(); + } + } + + public CLValue ToCLValue() + { + return new CLValue(ToBytes(), CLType.Any); + } + } +} diff --git a/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs index 3aa3154..700b882 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs @@ -17,11 +17,31 @@ public enum BidAddrTag /// /// Delegator BidAddr, /// - Delegator = 2, + DelegatedAccount = 2, + /// + /// BidAddr for delegated purse bid. + /// + DelegatedPurse = 3, /// /// BidAddr for auction credit. /// Credit = 4, + /// + /// BidAddr for reserved delegation account bid. + /// + ReservedDelegationAccount = 5, + /// + /// BidAddr for reserved delegation purse bid. + /// + ReservedDelegationPurse = 6, + /// + /// BidAddr for unbonding accounts. + /// + UnbondAccount = 7, + /// + /// BidAddr for unbonding purses. + /// + UnbondPurse = 8, } public class BidAddrKey : GlobalStateKey @@ -43,7 +63,12 @@ public class BidAddrKey : GlobalStateKey /// /// The delegator address. /// - public AccountHashKey Delegator { get; init; } + public AccountHashKey DelegatorAccount { get; init; } + + /// + /// The delegator purse address. + /// + public string DelegatorPurseAddress { get; init; } /// /// The era id. @@ -72,12 +97,19 @@ public BidAddrKey(string key) : base(key, KEYPREFIX) throw new Exception("Wrong key length for Validator BidAddr. Expected 33 bytes."); Validator = new AccountHashKey(bytes.Slice(1, 33)); break; - case (byte)BidAddrTag.Delegator: - Tag = BidAddrTag.Delegator; + case (byte)BidAddrTag.DelegatedAccount: + Tag = BidAddrTag.DelegatedAccount; + if (bytes.Length != 65) + throw new Exception("Wrong key length for DelegatedAccount BidAddr. Expected 65 bytes."); + Validator = new AccountHashKey(bytes.Slice(1, 33)); + DelegatorAccount = new AccountHashKey(bytes.Slice(33)); + break; + case (byte)BidAddrTag.DelegatedPurse: + Tag = BidAddrTag.DelegatedPurse; if (bytes.Length != 65) - throw new Exception("Wrong key length for Unified BidAddr. Expected 65 bytes."); + throw new Exception("Wrong key length for DelegatedPurse BidAddr. Expected 65 bytes."); Validator = new AccountHashKey(bytes.Slice(1, 33)); - Delegator = new AccountHashKey(bytes.Slice(33)); + DelegatorPurseAddress = Hex.ToHexString(bytes.Slice(33)); break; case (byte)BidAddrTag.Credit: Tag = BidAddrTag.Credit; @@ -86,6 +118,34 @@ public BidAddrKey(string key) : base(key, KEYPREFIX) Validator = new AccountHashKey(bytes.Slice(1, 33)); EraId = BitConverterExtensions.ToUInt64(bytes.Slice(33)); break; + case (byte)BidAddrTag.ReservedDelegationAccount: + Tag = BidAddrTag.ReservedDelegationAccount; + if (bytes.Length != 65) + throw new Exception("Wrong key length for ReservedDelegationAccount BidAddr. Expected 65 bytes."); + Validator = new AccountHashKey(bytes.Slice(1, 33)); + DelegatorAccount = new AccountHashKey(bytes.Slice(33)); + break; + case (byte)BidAddrTag.ReservedDelegationPurse: + Tag = BidAddrTag.ReservedDelegationPurse; + if (bytes.Length != 65) + throw new Exception("Wrong key length for ReservedDelegationPurse BidAddr. Expected 65 bytes."); + Validator = new AccountHashKey(bytes.Slice(1, 33)); + DelegatorPurseAddress = Hex.ToHexString(bytes.Slice(33)); + break; + case (byte)BidAddrTag.UnbondAccount: + Tag = BidAddrTag.UnbondAccount; + if (bytes.Length != 65) + throw new Exception("Wrong key length for UnbondAccount BidAddr. Expected 65 bytes."); + Validator = new AccountHashKey(bytes.Slice(1, 33)); + DelegatorAccount = new AccountHashKey(bytes.Slice(33)); + break; + case (byte)BidAddrTag.UnbondPurse: + Tag = BidAddrTag.UnbondPurse; + if (bytes.Length != 65) + throw new Exception("Wrong key length for UnbondPurse BidAddr. Expected 65 bytes."); + Validator = new AccountHashKey(bytes.Slice(1, 33)); + DelegatorPurseAddress = Hex.ToHexString(bytes.Slice(33)); + break; default: throw new Exception($"Wrong BidAddr tag '{bytes[0]}'."); } diff --git a/Casper.Network.SDK/Types/Reservation.cs b/Casper.Network.SDK/Types/Reservation.cs new file mode 100644 index 0000000..5ce9e51 --- /dev/null +++ b/Casper.Network.SDK/Types/Reservation.cs @@ -0,0 +1,46 @@ +using System.IO; +using System.Text.Json.Serialization; +using Casper.Network.SDK.ByteSerializers; + +namespace Casper.Network.SDK.Types +{ + /// + /// Represents a validator reserving a slot for specific delegator + /// + public class Reservation + { + /// + /// The validator public key. + /// + [JsonPropertyName("validator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey ValidatorPublicKey { get; init; } + + /// + /// The delegator public key or purse. + /// + [JsonPropertyName("delegator_kind")] + public DelegatorKind DelegatorKind { get; init; } + + /// + /// The delegation rate. + /// + /// + [JsonPropertyName("delegation_rate")] + public uint DelegationRate { get; init; } + + public byte[] ToBytes() + { + MemoryStream ms = new MemoryStream(); + ms.Write(this.DelegatorKind.ToBytes()); + ms.Write(ValidatorPublicKey.GetBytes()); + ms.WriteByte((byte)DelegationRate); + return ms.ToArray(); + } + + public CLValue ToCLValue() + { + return new CLValue(ToBytes(), CLType.Any); + } + } +} diff --git a/Casper.Network.SDK/Types/SeigniorageAllocation.cs b/Casper.Network.SDK/Types/SeigniorageAllocation.cs index 4c1ee7f..0734890 100644 --- a/Casper.Network.SDK/Types/SeigniorageAllocation.cs +++ b/Casper.Network.SDK/Types/SeigniorageAllocation.cs @@ -16,9 +16,9 @@ public class SeigniorageAllocation public bool IsDelegator { get; init; } /// - /// Public key of the delegator (null if not a delegator reward allocation). + /// Public key or purse of the delegator (null if not a delegator reward allocation). /// - public PublicKey DelegatorPublicKey { get; init; } + public DelegatorKind DelegatorKind { get; init; } /// /// Public key of the validator @@ -53,7 +53,7 @@ public override SeigniorageAllocation Read( reader.Read(); // start object - string delegatorPk = null; + DelegatorKind delegatorKind = null; string validatorPk = null; BigInteger amount = BigInteger.Zero; @@ -62,7 +62,7 @@ public override SeigniorageAllocation Read( var field = reader.GetString(); reader.Read(); if (field == "delegator_public_key") - delegatorPk = reader.GetString(); + delegatorKind = JsonSerializer.Deserialize(ref reader, options); else if (field == "validator_public_key") validatorPk = reader.GetString(); else if (field == "amount") @@ -75,7 +75,7 @@ public override SeigniorageAllocation Read( return new SeigniorageAllocation() { IsDelegator = propertyName?.ToLowerInvariant() == "delegator", - DelegatorPublicKey = delegatorPk != null ? PublicKey.FromHexString(delegatorPk) : null, + DelegatorKind = delegatorKind, ValidatorPublicKey = PublicKey.FromHexString(validatorPk), Amount = amount }; @@ -91,8 +91,8 @@ public override void Write( writer.WriteStartObject(); writer.WritePropertyName("Delegator"); writer.WriteStartObject(); - writer.WritePropertyName("delegator_public_key"); - writer.WriteStringValue(value.DelegatorPublicKey.ToString()); + writer.WritePropertyName("delegator_kind"); + JsonSerializer.Serialize(writer, value.DelegatorKind, options); writer.WritePropertyName("validator_public_key"); writer.WriteStringValue(value.ValidatorPublicKey.ToString()); writer.WritePropertyName("amount"); diff --git a/Casper.Network.SDK/Types/TransactionBuilder.cs b/Casper.Network.SDK/Types/TransactionBuilder.cs index 8b12b59..6a58017 100644 --- a/Casper.Network.SDK/Types/TransactionBuilder.cs +++ b/Casper.Network.SDK/Types/TransactionBuilder.cs @@ -1,62 +1,63 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Numerics; using Casper.Network.SDK.Utils; +using Org.BouncyCastle.Utilities.Encoders; namespace Casper.Network.SDK.Types { public partial class Transaction { - public abstract class TransactionV1Builder where T : TransactionV1Builder + public abstract class TransactionV1Builder where T : TransactionV1Builder { protected InitiatorAddr _from = null; protected string _chainName = null; protected DateTime? _timestamp = null; protected ulong _ttl = 1800000; //30m - protected byte _gasPriceTolerance = 1; - + + protected IPricingMode _pricingMode; protected ITransactionV1Target _invocationTarget; protected ITransactionV1EntryPoint _entryPoint; - protected TransactionCategory _category; protected ITransactionV1Scheduling _scheduling = TransactionScheduling.Standard; protected List _runtimeArgs = new(); - + public T From(PublicKey publicKey) { _from = Types.InitiatorAddr.FromPublicKey(publicKey); return (T)this; } - + public T From(AccountHashKey accountHashKey) { _from = Types.InitiatorAddr.FromAccountHash(accountHashKey); return (T)this; } - + public T ChainName(string chainName) { _chainName = chainName; return (T)this; } - + public T Timestamp(DateTime timestamp) { _timestamp = timestamp; return (T)this; } - + public T TTL(ulong ttl) { _ttl = ttl; return (T)this; } - - public T GasPriceTolerance(byte gasPriceTolerance) + + public T Payment(IPricingMode pricingMode) { - _gasPriceTolerance = gasPriceTolerance; + _pricingMode = pricingMode; return (T)this; } - + public virtual TransactionV1 Build() { var payload = new TransactionV1Payload() @@ -65,7 +66,7 @@ public virtual TransactionV1 Build() Timestamp = DateUtils.ToEpochTime(_timestamp.HasValue ? _timestamp.Value : DateTime.UtcNow), Ttl = _ttl, ChainName = _chainName, - PricingMode = Types.PricingMode.Fixed(_gasPriceTolerance), + PricingMode = _pricingMode, RuntimeArgs = _runtimeArgs, Target = _invocationTarget, EntryPoint = _entryPoint, @@ -74,7 +75,7 @@ public virtual TransactionV1 Build() return new TransactionV1(payload); } } - + public class NativeTransferBuilder : TransactionV1Builder { //specific tx properties @@ -86,39 +87,38 @@ public NativeTransferBuilder() { _invocationTarget = TransactionV1Target.Native; _entryPoint = TransactionV1EntryPoint.Transfer; - _category = TransactionCategory.Mint; } - + public NativeTransferBuilder Target(PublicKey publicKey) { _target = CLValue.PublicKey(publicKey); return this; } - + public NativeTransferBuilder Target(AccountHashKey accountHashKey) { _target = CLValue.ByteArray(accountHashKey.RawBytes); return this; } - + public NativeTransferBuilder Amount(ulong amount) { _amount = CLValue.U512(amount); return this; } - + public NativeTransferBuilder Amount(BigInteger amount) { _amount = CLValue.U512(amount); return this; } - + public NativeTransferBuilder Id(ulong id) { _idTransfer = id; return this; } - + public override TransactionV1 Build() { _runtimeArgs = new List(); @@ -128,203 +128,471 @@ public override TransactionV1 Build() { _runtimeArgs.Add(new NamedArg("id", CLValue.Option(CLValue.U64(_idTransfer.Value)))); } - + return base.Build(); } } - + public class NativeDelegateBuilder : TransactionV1Builder { //specific tx properties private CLValue _validator = null; private CLValue _amount = CLValue.U512((BigInteger)0); - + public NativeDelegateBuilder() { _invocationTarget = TransactionV1Target.Native; _entryPoint = TransactionV1EntryPoint.Delegate; - _category = TransactionCategory.Auction; } - + public NativeDelegateBuilder Validator(PublicKey publicKey) { _validator = CLValue.PublicKey(publicKey); return this; } - + public NativeDelegateBuilder Amount(ulong amount) { _amount = CLValue.U512(amount); return this; } - + public NativeDelegateBuilder Amount(BigInteger amount) { _amount = CLValue.U512(amount); return this; } - + public override TransactionV1 Build() { _runtimeArgs = new List(); _runtimeArgs.Add(new NamedArg("delegator", CLValue.PublicKey(_from.PublicKey))); _runtimeArgs.Add(new NamedArg("validator", _validator)); _runtimeArgs.Add(new NamedArg("amount", _amount)); - + return base.Build(); } } - + public class NativeUndelegateBuilder : TransactionV1Builder { //specific tx properties private CLValue _validator = null; private CLValue _amount = CLValue.U512((BigInteger)0); - + public NativeUndelegateBuilder() { _invocationTarget = TransactionV1Target.Native; _entryPoint = TransactionV1EntryPoint.Undelegate; - _category = TransactionCategory.Auction; } - + public NativeUndelegateBuilder Validator(PublicKey publicKey) { _validator = CLValue.PublicKey(publicKey); return this; } - + public NativeUndelegateBuilder Amount(ulong amount) { _amount = CLValue.U512(amount); return this; } - + public NativeUndelegateBuilder Amount(BigInteger amount) { _amount = CLValue.U512(amount); return this; } - + public override TransactionV1 Build() { _runtimeArgs = new List(); _runtimeArgs.Add(new NamedArg("delegator", CLValue.PublicKey(_from.PublicKey))); _runtimeArgs.Add(new NamedArg("validator", _validator)); _runtimeArgs.Add(new NamedArg("amount", _amount)); - + return base.Build(); } } - - public class ContractCallBuilder : TransactionV1Builder + + public class NativeRedelegateBuilder : TransactionV1Builder { - public ContractCallBuilder() + //specific tx properties + private CLValue _validator = null; + private CLValue _newValidator = null; + private CLValue _amount = CLValue.U512((BigInteger)0); + + public NativeRedelegateBuilder() { - _category = TransactionCategory.Small; + _invocationTarget = TransactionV1Target.Native; + _entryPoint = TransactionV1EntryPoint.Redelegate; } - - public ContractCallBuilder ByHash(string contractHash) + + public NativeRedelegateBuilder Validator(PublicKey publicKey) { - _invocationTarget = TransactionV1Target.StoredByHash(contractHash); + _validator = CLValue.PublicKey(publicKey); return this; } - - public ContractCallBuilder ByName(string name) + + public NativeRedelegateBuilder NewValidator(PublicKey publicKey) { - _invocationTarget = TransactionV1Target.StoredByName(name); + _newValidator = CLValue.PublicKey(publicKey); return this; } - - public ContractCallBuilder ByPackageHash(string contractHash) + + public NativeRedelegateBuilder Amount(ulong amount) { - _invocationTarget = TransactionV1Target.StoredByPackageHash(contractHash); + _amount = CLValue.U512(amount); return this; } - - public ContractCallBuilder ByPackageName(string name) + + public NativeRedelegateBuilder Amount(BigInteger amount) { - _invocationTarget = TransactionV1Target.StoredByPackageName(name); + _amount = CLValue.U512(amount); return this; } - - public ContractCallBuilder EntryPoint(string name) + + public override TransactionV1 Build() { - _entryPoint = TransactionV1EntryPoint.Custom(name); + _runtimeArgs = new List(); + _runtimeArgs.Add(new NamedArg("delegator", CLValue.PublicKey(_from.PublicKey))); + _runtimeArgs.Add(new NamedArg("validator", _validator)); + _runtimeArgs.Add(new NamedArg("new_validator", _validator)); + _runtimeArgs.Add(new NamedArg("amount", _amount)); + + return base.Build(); + } + } + + public class NativeActivateBidBuilder : TransactionV1Builder + { + //specific tx properties + private CLValue _validator = null; + + public NativeActivateBidBuilder() + { + _invocationTarget = TransactionV1Target.Native; + _entryPoint = TransactionV1EntryPoint.ActivateBid; + } + + public NativeActivateBidBuilder Validator(PublicKey publicKey) + { + _validator = CLValue.PublicKey(publicKey); return this; } - - public ContractCallBuilder InstallCategory() + + public override TransactionV1 Build() { - _category = TransactionCategory.Large; + _runtimeArgs = new List(); + _runtimeArgs.Add(new NamedArg("validator", _validator)); + return base.Build(); + } + } + + public class NativeChangeBidPublicKeyBuilder : TransactionV1Builder + { + //specific tx properties + private CLValue _public_key = null; + private CLValue _new_public_key = null; + + public NativeChangeBidPublicKeyBuilder() + { + _invocationTarget = TransactionV1Target.Native; + _entryPoint = TransactionV1EntryPoint.ChangeBidPublicKey; + } + + public NativeChangeBidPublicKeyBuilder PublicKey(PublicKey publicKey) + { + _public_key = CLValue.PublicKey(publicKey); return this; } - - public ContractCallBuilder LargeCategory() + + public NativeChangeBidPublicKeyBuilder NewPublicKey(PublicKey publicKey) { - _category = TransactionCategory.Large; + _new_public_key = CLValue.PublicKey(publicKey); return this; } + + public override TransactionV1 Build() + { + _runtimeArgs = new List(); + _runtimeArgs.Add(new NamedArg("public_key", _public_key)); + _runtimeArgs.Add(new NamedArg("new_public_key", _new_public_key)); + return base.Build(); + } + } + + public class NativeAddBidBuilder : TransactionV1Builder + { + //specific tx properties + private CLValue _validator = null; + private CLValue _amount = CLValue.U512((BigInteger)0); + private CLValue _delegationRate = CLValue.U8(100); + private CLValue _minimumDelegationAmount = CLValue.OptionNone(CLType.U64); + private CLValue _maximumDelegationAmount = CLValue.OptionNone(CLType.U64); + private CLValue _reservedSlots = null; - public ContractCallBuilder MediumCategory() + public NativeAddBidBuilder() + { + _invocationTarget = TransactionV1Target.Native; + _entryPoint = TransactionV1EntryPoint.AddBid; + } + + public NativeAddBidBuilder Validator(PublicKey publicKey) { - _category = TransactionCategory.Medium; + _validator = CLValue.PublicKey(publicKey); return this; } - - public ContractCallBuilder SmallCategory() + + public NativeAddBidBuilder Amount(ulong amount) { - _category = TransactionCategory.Small; + _amount = CLValue.U512(amount); + return this; + } + + public NativeAddBidBuilder Amount(BigInteger amount) + { + _amount = CLValue.U512(amount); + return this; + } + + public NativeAddBidBuilder DelegationRate(byte delegationRate) + { + _delegationRate = CLValue.U8(delegationRate); + return this; + } + + public NativeAddBidBuilder MinimumDelegationAmount(ulong minimumDelegationAmount) + { + _minimumDelegationAmount = CLValue.Option(CLValue.U64(minimumDelegationAmount)); + _minimumDelegationAmount = CLValue.U64(minimumDelegationAmount); + return this; + } + + public NativeAddBidBuilder MaximumDelegationAmount(ulong maximumDelegationAmount) + { + _maximumDelegationAmount = CLValue.Option(CLValue.U64(maximumDelegationAmount)); + _maximumDelegationAmount = CLValue.U64(maximumDelegationAmount); + return this; + } + + public NativeAddBidBuilder ReservedSlots(uint reservedSlots) + { + _reservedSlots = CLValue.U32(reservedSlots); + // _reservedSlots = CLValue.OptionNone(CLType.U32); + return this; + } + + public override TransactionV1 Build() + { + _runtimeArgs = new List(); + _runtimeArgs.Add(new NamedArg("public_key", _validator)); + _runtimeArgs.Add(new NamedArg("amount", _amount)); + _runtimeArgs.Add(new NamedArg("delegation_rate", _delegationRate)); + _runtimeArgs.Add(new NamedArg("minimum_delegation_amount", _minimumDelegationAmount)); + _runtimeArgs.Add(new NamedArg("maximum_delegation_amount", _maximumDelegationAmount)); + if(_reservedSlots != null) + _runtimeArgs.Add(new NamedArg("reserved_slots", _reservedSlots)); + return base.Build(); + } + } + + public class NativeWithdrawBidBuilder : TransactionV1Builder + { + //specific tx properties + private CLValue _validator = null; + private CLValue _amount = CLValue.U512((BigInteger)0); + + public NativeWithdrawBidBuilder() + { + _invocationTarget = TransactionV1Target.Native; + _entryPoint = TransactionV1EntryPoint.WithdrawBid; + } + + public NativeWithdrawBidBuilder Validator(PublicKey publicKey) + { + _validator = CLValue.PublicKey(publicKey); + return this; + } + + public NativeWithdrawBidBuilder Amount(ulong amount) + { + _amount = CLValue.U512(amount); + return this; + } + + public NativeWithdrawBidBuilder Amount(BigInteger amount) + { + _amount = CLValue.U512(amount); + return this; + } + + public override TransactionV1 Build() + { + _runtimeArgs = new List(); + _runtimeArgs.Add(new NamedArg("public_key", _validator)); + _runtimeArgs.Add(new NamedArg("amount", _amount)); + return base.Build(); + } + } + + public class NativeAddReservationsBuilder : TransactionV1Builder + { + //specific tx properties + private List _reservations = null; + + public NativeAddReservationsBuilder() + { + _invocationTarget = TransactionV1Target.Native; + _entryPoint = TransactionV1EntryPoint.AddReservations; + } + + public NativeAddReservationsBuilder Reservations(List reservations) + { + _reservations = reservations; + return this; + } + + public override TransactionV1 Build() + { + var list = _reservations.Select(r => r.ToCLValue()); + _runtimeArgs.Add(new NamedArg("reservations", CLValue.List(list.ToArray()))); + return base.Build(); + } + } + + public class NativeCancelReservationsBuilder : TransactionV1Builder + { + //specific tx properties + private PublicKey _validator = null; + private List _delegators = null; + + public NativeCancelReservationsBuilder() + { + _invocationTarget = TransactionV1Target.Native; + _entryPoint = TransactionV1EntryPoint.CancelReservations; + } + + public NativeCancelReservationsBuilder Validator(PublicKey validator) + { + _validator = validator; return this; } + public NativeCancelReservationsBuilder Delegators(List delegators) + { + _delegators = delegators; + return this; + } + + public override TransactionV1 Build() + { + _runtimeArgs.Add(new NamedArg("validator", CLValue.PublicKey(_validator))); + // var list = _delegators.Select(r => r.ToCLValue()); + var list = _delegators.Select(r => CLValue.PublicKey(r.PublicKey)); + _runtimeArgs.Add(new NamedArg("delegators", CLValue.List(list.ToArray()))); + return base.Build(); + } + } + + public class ContractCallBuilder : TransactionV1Builder + { + private ulong _transferredValue = 0; + + public ContractCallBuilder() + { + } + + public ContractCallBuilder ByHash(string contractHash) + { + _invocationTarget = TransactionV1Target.StoredByHash(contractHash, _transferredValue); + return this; + } + + public ContractCallBuilder ByName(string name) + { + _invocationTarget = TransactionV1Target.StoredByName(name, _transferredValue); + return this; + } + + public ContractCallBuilder ByPackageHash(string contractHash, UInt32? version = null) + { + _invocationTarget = TransactionV1Target.StoredByPackageHash(contractHash, version, _transferredValue); + return this; + } + + public ContractCallBuilder ByPackageName(string name, UInt32? version = null) + { + _invocationTarget = TransactionV1Target.StoredByPackageName(name, version, _transferredValue); + return this; + } + + public ContractCallBuilder TransferredValue(ulong transferredValue) + { + _transferredValue = transferredValue; + if (_invocationTarget is StoredTransactionV1Target storedTransactionV1Target) + storedTransactionV1Target.TransferredValue = transferredValue; + return this; + } + + public ContractCallBuilder EntryPoint(string name) + { + _entryPoint = TransactionV1EntryPoint.Custom(name); + return this; + } + public ContractCallBuilder RuntimeArgs(List args) { _runtimeArgs = args; return this; } } - + public class SessionBuilder : TransactionV1Builder { + private bool _isInstallOrUpgrade = false; + private ulong _transferredValue = 0; + private string _seed = null; private byte[] _wasm = null; - + public SessionBuilder() { - _category = TransactionCategory.InstallUpgrade; _entryPoint = TransactionV1EntryPoint.Call; } - + public SessionBuilder Wasm(byte[] wasmBytes) { - _invocationTarget = TransactionV1Target.Session(wasmBytes); - return this; - } - - public SessionBuilder InstallCategory() - { - _category = TransactionCategory.Large; + var target = TransactionV1Target.Session(wasmBytes, _transferredValue); + target.IsInstallUpgrade = _isInstallOrUpgrade; + target.Seed = _seed; + _invocationTarget = target; return this; } - - public SessionBuilder LargeCategory() + + public SessionBuilder InstallOrUpgrade() { - _category = TransactionCategory.Large; + _isInstallOrUpgrade = true; + if (_invocationTarget is SessionTransactionV1Target sessionTarget) + sessionTarget.IsInstallUpgrade = true; return this; } - - public SessionBuilder MediumCategory() + + public SessionBuilder TransferredValue(ulong transferredValue) { - _category = TransactionCategory.Medium; + _transferredValue = transferredValue; + if (_invocationTarget is SessionTransactionV1Target sessionTarget) + sessionTarget.TransferredValue = transferredValue; return this; } - - public SessionBuilder SmallCategory() + + public SessionBuilder Seed(string seed) { - _category = TransactionCategory.Small; + _seed = seed; + if (_invocationTarget is SessionTransactionV1Target sessionTarget) + sessionTarget.Seed = seed; return this; } - + public SessionBuilder RuntimeArgs(List args) { _runtimeArgs = args; @@ -332,4 +600,4 @@ public SessionBuilder RuntimeArgs(List args) } } } -} +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs b/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs index 6c30e84..dbd25c6 100644 --- a/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs +++ b/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs @@ -192,6 +192,12 @@ public class TransactionV1EntryPoint public static ITransactionV1EntryPoint Call => new NativeTransactionV1EntryPoint(NativeEntryPoint.Call); + public static ITransactionV1EntryPoint AddReservations => + new NativeTransactionV1EntryPoint(NativeEntryPoint.AddReservations); + + public static ITransactionV1EntryPoint CancelReservations => + new NativeTransactionV1EntryPoint(NativeEntryPoint.CancelReservations); + public static ITransactionV1EntryPoint Custom(string name) => new CustomTransactionV1EntryPoint(name); diff --git a/Casper.Network.SDK/Types/TransactionV1Target.cs b/Casper.Network.SDK/Types/TransactionV1Target.cs index be8e1e5..1dbc990 100644 --- a/Casper.Network.SDK/Types/TransactionV1Target.cs +++ b/Casper.Network.SDK/Types/TransactionV1Target.cs @@ -212,7 +212,7 @@ public byte[] ToBytes() public class StoredTransactionV1Target : ITransactionV1Target { [JsonPropertyName("id")] - public IInvocationTarget Id { get; init; } + public IInvocationTarget Id { get; set; } /// /// Targeted Casper VM version. @@ -224,7 +224,7 @@ public class StoredTransactionV1Target : ITransactionV1Target /// The amount of motes to transfer before code is executed. /// [JsonPropertyName("transferred_value")] - public ulong TransferredValue { get; init; } + public ulong TransferredValue { get; set; } const ushort TAG_FIELD_INDEX = 0; const byte STORED_VARIANT = 1; @@ -248,14 +248,14 @@ public class SessionTransactionV1Target : ITransactionV1Target /// Flag determining if the Wasm is an install/upgrade. /// [JsonPropertyName("is_install_upgrade")] - public bool IsInstallUpgrade { get; init; } + public bool IsInstallUpgrade { get; set; } /// /// Wasm bytes for a Session transaction type. /// [JsonPropertyName("module_bytes")] [JsonConverter(typeof(HexBytesConverter))] - public byte[] ModuleBytes { get; init; } + public byte[] ModuleBytes { get; set; } /// /// Targeted Casper VM version. @@ -270,13 +270,13 @@ public class SessionTransactionV1Target : ITransactionV1Target /// that can be transferred from the caller account to the session account. /// [JsonPropertyName("transferred_value")] - public ulong TransferredValue { get; init; } + public ulong TransferredValue { get; set; } /// /// The seed for the session code that is used for an installer. /// [JsonPropertyName("seed")] - public string Seed { get; init; } + public string Seed { get; set; } const ushort TAG_FIELD_INDEX = 0; const byte SESSION_VARIANT = 2; @@ -302,43 +302,48 @@ public class TransactionV1Target { public static ITransactionV1Target Native => new NativeTransactionV1Target(); - public static ITransactionV1Target StoredByHash(string contractHash) + public static StoredTransactionV1Target StoredByHash(string contractHash, ulong transferredValue = 0) { return new StoredTransactionV1Target() { - Id = new ByHashInvocationTarget { Hash = contractHash } + Id = new ByHashInvocationTarget { Hash = contractHash }, + TransferredValue = transferredValue, }; } - public static ITransactionV1Target StoredByName(string name) + public static StoredTransactionV1Target StoredByName(string name, ulong transferredValue = 0) { return new StoredTransactionV1Target() { - Id = new ByNameInvocationTarget { Name = name } + Id = new ByNameInvocationTarget { Name = name }, + TransferredValue = transferredValue, }; } - public static ITransactionV1Target StoredByPackageHash(string packageHash, UInt32? version = null) + public static StoredTransactionV1Target StoredByPackageHash(string packageHash, UInt32? version = null, ulong transferredValue = 0) { return new StoredTransactionV1Target() { - Id = new ByPackageHashInvocationTarget { Hash = packageHash, Version = version } + Id = new ByPackageHashInvocationTarget { Hash = packageHash, Version = version }, + TransferredValue = transferredValue, }; } - public static ITransactionV1Target StoredByPackageName(string name, UInt32? version = null) + public static StoredTransactionV1Target StoredByPackageName(string name, UInt32? version = null, ulong transferredValue = 0) { return new StoredTransactionV1Target() { - Id = new ByPackageNameInvocationTarget() { Name = name, Version = version } + Id = new ByPackageNameInvocationTarget() { Name = name, Version = version }, + TransferredValue = transferredValue, }; } - public static ITransactionV1Target Session(byte[] moduleBytes) + public static SessionTransactionV1Target Session(byte[] moduleBytes, ulong transferredValue = 0) { return new SessionTransactionV1Target() { ModuleBytes = moduleBytes, + TransferredValue = transferredValue, }; } @@ -366,6 +371,7 @@ public override ITransactionV1Target Read( ITransactionV1Target transactionTarget = null; IInvocationTarget id = null; string module_bytes = null; + ulong transferredValue = 0;; TransactionRuntime runtime = TransactionRuntime.VmCasperV1; reader.Read(); // skip start object @@ -386,6 +392,10 @@ public override ITransactionV1Target Read( id = JsonSerializer.Deserialize(ref reader, options); reader.Read(); break; + case "transferred_value": + transferredValue = reader.GetUInt64(); + reader.Read(); + break; case "runtime": runtime = EnumCompat.Parse(reader.GetString()); reader.Read(); // skip end object @@ -398,6 +408,7 @@ public override ITransactionV1Target Read( transactionTarget = new StoredTransactionV1Target() { Id = id, + TransferredValue = transferredValue, Runtime = runtime, }; break; @@ -412,6 +423,10 @@ public override ITransactionV1Target Read( case "module_bytes": module_bytes = reader.GetString(); break; + case "transferred_value": + transferredValue = reader.GetUInt64(); + reader.Read(); + break; case "runtime": runtime = EnumCompat.Parse(reader.GetString()); break; @@ -423,6 +438,7 @@ public override ITransactionV1Target Read( transactionTarget = new SessionTransactionV1Target() { ModuleBytes = Hex.Decode(module_bytes), + TransferredValue = transferredValue, Runtime = runtime, }; break; @@ -451,6 +467,8 @@ public override void Write( writer.WriteStartObject("Stored"); writer.WritePropertyName("id"); JsonSerializer.Serialize(writer, storedTarget.Id); + writer.WritePropertyName("transferred_value"); + writer.WriteNumberValue(storedTarget.TransferredValue); writer.WriteString("runtime", storedTarget.Runtime.ToString()); writer.WriteEndObject(); writer.WriteEndObject(); @@ -459,6 +477,8 @@ public override void Write( writer.WriteStartObject(); writer.WriteStartObject("Session"); writer.WriteString("module_bytes", Hex.ToHexString(sessionTarget.ModuleBytes)); + writer.WritePropertyName("transferred_value"); + writer.WriteNumberValue(sessionTarget.TransferredValue); writer.WriteString("runtime", sessionTarget.Runtime.ToString()); writer.WriteEndObject(); writer.WriteEndObject(); diff --git a/Docs/Examples/ListRewards/Program.cs b/Docs/Examples/ListRewards/Program.cs index 2b81736..2897861 100644 --- a/Docs/Examples/ListRewards/Program.cs +++ b/Docs/Examples/ListRewards/Program.cs @@ -40,7 +40,7 @@ public async static Task GetEraSummary() $"{rewards.ToString("N9"),35} $CSPR"); var delegators = group.Where(a => a.IsDelegator) - .GroupBy(a => a.DelegatorPublicKey); + .GroupBy(a => a.DelegatorKind.PublicKey); foreach (var delegatorAllocations in delegators) { var delegatorRewards = delegatorAllocations.Sum(a => (double)a.Amount); From 1fcb33a1612cdc9c9dfa653a8853babc29d8fc3c Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 10 Dec 2024 17:04:36 +0100 Subject: [PATCH 092/126] Added missing native entry points to TransactionBuilder class Signed-off-by: David Hernando --- .../Types/TransactionBuilder.cs | 332 +++++++++++------- 1 file changed, 197 insertions(+), 135 deletions(-) diff --git a/Casper.Network.SDK/Types/TransactionBuilder.cs b/Casper.Network.SDK/Types/TransactionBuilder.cs index 6a58017..026bdb5 100644 --- a/Casper.Network.SDK/Types/TransactionBuilder.cs +++ b/Casper.Network.SDK/Types/TransactionBuilder.cs @@ -9,28 +9,38 @@ namespace Casper.Network.SDK.Types { public partial class Transaction { + [AttributeUsage(AttributeTargets.Field, Inherited = true, AllowMultiple = false)] + public class RequiredArgAttribute : Attribute + { + } + public abstract class TransactionV1Builder where T : TransactionV1Builder { - protected InitiatorAddr _from = null; - protected string _chainName = null; - protected DateTime? _timestamp = null; + [RequiredArg] + protected InitiatorAddr _initiatorAddr; + [RequiredArg] + protected string _chainName; + protected DateTime? _timestamp; protected ulong _ttl = 1800000; //30m - + [RequiredArg] protected IPricingMode _pricingMode; + [RequiredArg] protected ITransactionV1Target _invocationTarget; + [RequiredArg] protected ITransactionV1EntryPoint _entryPoint; protected ITransactionV1Scheduling _scheduling = TransactionScheduling.Standard; + [RequiredArg] protected List _runtimeArgs = new(); public T From(PublicKey publicKey) { - _from = Types.InitiatorAddr.FromPublicKey(publicKey); + _initiatorAddr = Types.InitiatorAddr.FromPublicKey(publicKey); return (T)this; } public T From(AccountHashKey accountHashKey) { - _from = Types.InitiatorAddr.FromAccountHash(accountHashKey); + _initiatorAddr = Types.InitiatorAddr.FromAccountHash(accountHashKey); return (T)this; } @@ -57,12 +67,26 @@ public T Payment(IPricingMode pricingMode) _pricingMode = pricingMode; return (T)this; } + + protected void ValidateRequiredProperties() + { + var missingProperties = this.GetType() + .GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) + .Where(field => Attribute.IsDefined(field, typeof(RequiredArgAttribute)) && field.GetValue(this) == null) + .Select(field => field.Name) + .ToList(); + + if (missingProperties.Any()) + { + throw new InvalidOperationException($"The following required properties are missing: {string.Join(", ", missingProperties)}"); + } + } public virtual TransactionV1 Build() { var payload = new TransactionV1Payload() { - InitiatorAddr = _from, + InitiatorAddr = _initiatorAddr, Timestamp = DateUtils.ToEpochTime(_timestamp.HasValue ? _timestamp.Value : DateTime.UtcNow), Ttl = _ttl, ChainName = _chainName, @@ -79,9 +103,11 @@ public virtual TransactionV1 Build() public class NativeTransferBuilder : TransactionV1Builder { //specific tx properties - private CLValue _target = null; + [RequiredArg] + private CLValue _target; + [RequiredArg] private CLValue _amount = CLValue.U512((BigInteger)0); - private ulong? _idTransfer = null; + private ulong? _idTransfer; public NativeTransferBuilder() { @@ -121,6 +147,8 @@ public NativeTransferBuilder Id(ulong id) public override TransactionV1 Build() { + ValidateRequiredProperties(); + _runtimeArgs = new List(); _runtimeArgs.Add(new NamedArg("target", _target)); _runtimeArgs.Add(new NamedArg("amount", _amount)); @@ -132,73 +160,113 @@ public override TransactionV1 Build() return base.Build(); } } - - public class NativeDelegateBuilder : TransactionV1Builder + + public class NativeAddBidBuilder : TransactionV1Builder { //specific tx properties - private CLValue _validator = null; - private CLValue _amount = CLValue.U512((BigInteger)0); - - public NativeDelegateBuilder() + [RequiredArg] + private CLValue _validator; + [RequiredArg] + private CLValue _amount; + [RequiredArg] + private CLValue _delegationRate; + private CLValue _minimumDelegationAmount; + private CLValue _maximumDelegationAmount; + private CLValue _reservedSlots; + + public NativeAddBidBuilder() { _invocationTarget = TransactionV1Target.Native; - _entryPoint = TransactionV1EntryPoint.Delegate; + _entryPoint = TransactionV1EntryPoint.AddBid; } - public NativeDelegateBuilder Validator(PublicKey publicKey) + public NativeAddBidBuilder Validator(PublicKey publicKey) { _validator = CLValue.PublicKey(publicKey); return this; } - public NativeDelegateBuilder Amount(ulong amount) + public NativeAddBidBuilder Amount(ulong amount) { _amount = CLValue.U512(amount); return this; } - public NativeDelegateBuilder Amount(BigInteger amount) + public NativeAddBidBuilder Amount(BigInteger amount) { _amount = CLValue.U512(amount); return this; } + public NativeAddBidBuilder DelegationRate(byte delegationRate) + { + _delegationRate = CLValue.U8(delegationRate); + return this; + } + + public NativeAddBidBuilder MinimumDelegationAmount(ulong minimumDelegationAmount) + { + _minimumDelegationAmount = CLValue.U64(minimumDelegationAmount); + return this; + } + + public NativeAddBidBuilder MaximumDelegationAmount(ulong maximumDelegationAmount) + { + _maximumDelegationAmount = CLValue.U64(maximumDelegationAmount); + return this; + } + + public NativeAddBidBuilder ReservedSlots(uint reservedSlots) + { + _reservedSlots = CLValue.U32(reservedSlots); + return this; + } + public override TransactionV1 Build() { + ValidateRequiredProperties(); + _runtimeArgs = new List(); - _runtimeArgs.Add(new NamedArg("delegator", CLValue.PublicKey(_from.PublicKey))); - _runtimeArgs.Add(new NamedArg("validator", _validator)); + _runtimeArgs.Add(new NamedArg("public_key", _validator)); _runtimeArgs.Add(new NamedArg("amount", _amount)); - + _runtimeArgs.Add(new NamedArg("delegation_rate", _delegationRate)); + if(_minimumDelegationAmount != null) + _runtimeArgs.Add(new NamedArg("minimum_delegation_amount", _minimumDelegationAmount)); + if(_maximumDelegationAmount != null) + _runtimeArgs.Add(new NamedArg("maximum_delegation_amount", _maximumDelegationAmount)); + if(_reservedSlots != null) + _runtimeArgs.Add(new NamedArg("reserved_slots", _reservedSlots)); return base.Build(); } } - public class NativeUndelegateBuilder : TransactionV1Builder + public class NativeWithdrawBidBuilder : TransactionV1Builder { //specific tx properties - private CLValue _validator = null; + [RequiredArg] + private CLValue _validator; + [RequiredArg] private CLValue _amount = CLValue.U512((BigInteger)0); - public NativeUndelegateBuilder() + public NativeWithdrawBidBuilder() { _invocationTarget = TransactionV1Target.Native; - _entryPoint = TransactionV1EntryPoint.Undelegate; + _entryPoint = TransactionV1EntryPoint.WithdrawBid; } - public NativeUndelegateBuilder Validator(PublicKey publicKey) + public NativeWithdrawBidBuilder Validator(PublicKey publicKey) { _validator = CLValue.PublicKey(publicKey); return this; } - public NativeUndelegateBuilder Amount(ulong amount) + public NativeWithdrawBidBuilder Amount(ulong amount) { _amount = CLValue.U512(amount); return this; } - public NativeUndelegateBuilder Amount(BigInteger amount) + public NativeWithdrawBidBuilder Amount(BigInteger amount) { _amount = CLValue.U512(amount); return this; @@ -206,47 +274,42 @@ public NativeUndelegateBuilder Amount(BigInteger amount) public override TransactionV1 Build() { + ValidateRequiredProperties(); + _runtimeArgs = new List(); - _runtimeArgs.Add(new NamedArg("delegator", CLValue.PublicKey(_from.PublicKey))); - _runtimeArgs.Add(new NamedArg("validator", _validator)); + _runtimeArgs.Add(new NamedArg("public_key", _validator)); _runtimeArgs.Add(new NamedArg("amount", _amount)); - return base.Build(); } } - public class NativeRedelegateBuilder : TransactionV1Builder + public class NativeDelegateBuilder : TransactionV1Builder { //specific tx properties - private CLValue _validator = null; - private CLValue _newValidator = null; + [RequiredArg] + private CLValue _validator; + [RequiredArg] private CLValue _amount = CLValue.U512((BigInteger)0); - public NativeRedelegateBuilder() + public NativeDelegateBuilder() { _invocationTarget = TransactionV1Target.Native; - _entryPoint = TransactionV1EntryPoint.Redelegate; + _entryPoint = TransactionV1EntryPoint.Delegate; } - public NativeRedelegateBuilder Validator(PublicKey publicKey) + public NativeDelegateBuilder Validator(PublicKey publicKey) { _validator = CLValue.PublicKey(publicKey); return this; } - public NativeRedelegateBuilder NewValidator(PublicKey publicKey) - { - _newValidator = CLValue.PublicKey(publicKey); - return this; - } - - public NativeRedelegateBuilder Amount(ulong amount) + public NativeDelegateBuilder Amount(ulong amount) { _amount = CLValue.U512(amount); return this; } - public NativeRedelegateBuilder Amount(BigInteger amount) + public NativeDelegateBuilder Amount(BigInteger amount) { _amount = CLValue.U512(amount); return this; @@ -254,184 +317,177 @@ public NativeRedelegateBuilder Amount(BigInteger amount) public override TransactionV1 Build() { + ValidateRequiredProperties(); + _runtimeArgs = new List(); - _runtimeArgs.Add(new NamedArg("delegator", CLValue.PublicKey(_from.PublicKey))); + _runtimeArgs.Add(new NamedArg("delegator", CLValue.PublicKey(_initiatorAddr.PublicKey))); _runtimeArgs.Add(new NamedArg("validator", _validator)); - _runtimeArgs.Add(new NamedArg("new_validator", _validator)); _runtimeArgs.Add(new NamedArg("amount", _amount)); return base.Build(); } } - public class NativeActivateBidBuilder : TransactionV1Builder + public class NativeUndelegateBuilder : TransactionV1Builder { //specific tx properties - private CLValue _validator = null; + [RequiredArg] + private CLValue _validator; + [RequiredArg] + private CLValue _amount = CLValue.U512((BigInteger)0); - public NativeActivateBidBuilder() + public NativeUndelegateBuilder() { _invocationTarget = TransactionV1Target.Native; - _entryPoint = TransactionV1EntryPoint.ActivateBid; + _entryPoint = TransactionV1EntryPoint.Undelegate; } - public NativeActivateBidBuilder Validator(PublicKey publicKey) + public NativeUndelegateBuilder Validator(PublicKey publicKey) { _validator = CLValue.PublicKey(publicKey); return this; } - public override TransactionV1 Build() - { - _runtimeArgs = new List(); - _runtimeArgs.Add(new NamedArg("validator", _validator)); - return base.Build(); - } - } - - public class NativeChangeBidPublicKeyBuilder : TransactionV1Builder - { - //specific tx properties - private CLValue _public_key = null; - private CLValue _new_public_key = null; - - public NativeChangeBidPublicKeyBuilder() - { - _invocationTarget = TransactionV1Target.Native; - _entryPoint = TransactionV1EntryPoint.ChangeBidPublicKey; - } - - public NativeChangeBidPublicKeyBuilder PublicKey(PublicKey publicKey) + public NativeUndelegateBuilder Amount(ulong amount) { - _public_key = CLValue.PublicKey(publicKey); + _amount = CLValue.U512(amount); return this; } - public NativeChangeBidPublicKeyBuilder NewPublicKey(PublicKey publicKey) + public NativeUndelegateBuilder Amount(BigInteger amount) { - _new_public_key = CLValue.PublicKey(publicKey); + _amount = CLValue.U512(amount); return this; } public override TransactionV1 Build() { + ValidateRequiredProperties(); + _runtimeArgs = new List(); - _runtimeArgs.Add(new NamedArg("public_key", _public_key)); - _runtimeArgs.Add(new NamedArg("new_public_key", _new_public_key)); + _runtimeArgs.Add(new NamedArg("delegator", CLValue.PublicKey(_initiatorAddr.PublicKey))); + _runtimeArgs.Add(new NamedArg("validator", _validator)); + _runtimeArgs.Add(new NamedArg("amount", _amount)); + return base.Build(); } } - public class NativeAddBidBuilder : TransactionV1Builder + public class NativeRedelegateBuilder : TransactionV1Builder { //specific tx properties - private CLValue _validator = null; + [RequiredArg] + private CLValue _validator; + [RequiredArg] + private CLValue _newValidator; + [RequiredArg] private CLValue _amount = CLValue.U512((BigInteger)0); - private CLValue _delegationRate = CLValue.U8(100); - private CLValue _minimumDelegationAmount = CLValue.OptionNone(CLType.U64); - private CLValue _maximumDelegationAmount = CLValue.OptionNone(CLType.U64); - private CLValue _reservedSlots = null; - - public NativeAddBidBuilder() + + public NativeRedelegateBuilder() { _invocationTarget = TransactionV1Target.Native; - _entryPoint = TransactionV1EntryPoint.AddBid; + _entryPoint = TransactionV1EntryPoint.Redelegate; } - public NativeAddBidBuilder Validator(PublicKey publicKey) + public NativeRedelegateBuilder Validator(PublicKey publicKey) { _validator = CLValue.PublicKey(publicKey); return this; } - public NativeAddBidBuilder Amount(ulong amount) + public NativeRedelegateBuilder NewValidator(PublicKey publicKey) { - _amount = CLValue.U512(amount); + _newValidator = CLValue.PublicKey(publicKey); return this; } - public NativeAddBidBuilder Amount(BigInteger amount) + public NativeRedelegateBuilder Amount(ulong amount) { _amount = CLValue.U512(amount); return this; } - public NativeAddBidBuilder DelegationRate(byte delegationRate) + public NativeRedelegateBuilder Amount(BigInteger amount) { - _delegationRate = CLValue.U8(delegationRate); + _amount = CLValue.U512(amount); return this; } - public NativeAddBidBuilder MinimumDelegationAmount(ulong minimumDelegationAmount) + public override TransactionV1 Build() { - _minimumDelegationAmount = CLValue.Option(CLValue.U64(minimumDelegationAmount)); - _minimumDelegationAmount = CLValue.U64(minimumDelegationAmount); - return this; + ValidateRequiredProperties(); + + _runtimeArgs = new List(); + _runtimeArgs.Add(new NamedArg("delegator", CLValue.PublicKey(_initiatorAddr.PublicKey))); + _runtimeArgs.Add(new NamedArg("validator", _validator)); + _runtimeArgs.Add(new NamedArg("new_validator", _newValidator)); + _runtimeArgs.Add(new NamedArg("amount", _amount)); + + return base.Build(); } + } - public NativeAddBidBuilder MaximumDelegationAmount(ulong maximumDelegationAmount) + public class NativeActivateBidBuilder : TransactionV1Builder + { + //specific tx properties + [RequiredArg] + private CLValue _validator; + + public NativeActivateBidBuilder() { - _maximumDelegationAmount = CLValue.Option(CLValue.U64(maximumDelegationAmount)); - _maximumDelegationAmount = CLValue.U64(maximumDelegationAmount); - return this; + _invocationTarget = TransactionV1Target.Native; + _entryPoint = TransactionV1EntryPoint.ActivateBid; } - public NativeAddBidBuilder ReservedSlots(uint reservedSlots) + public NativeActivateBidBuilder Validator(PublicKey publicKey) { - _reservedSlots = CLValue.U32(reservedSlots); - // _reservedSlots = CLValue.OptionNone(CLType.U32); + _validator = CLValue.PublicKey(publicKey); return this; } public override TransactionV1 Build() { + ValidateRequiredProperties(); + _runtimeArgs = new List(); - _runtimeArgs.Add(new NamedArg("public_key", _validator)); - _runtimeArgs.Add(new NamedArg("amount", _amount)); - _runtimeArgs.Add(new NamedArg("delegation_rate", _delegationRate)); - _runtimeArgs.Add(new NamedArg("minimum_delegation_amount", _minimumDelegationAmount)); - _runtimeArgs.Add(new NamedArg("maximum_delegation_amount", _maximumDelegationAmount)); - if(_reservedSlots != null) - _runtimeArgs.Add(new NamedArg("reserved_slots", _reservedSlots)); + _runtimeArgs.Add(new NamedArg("validator", _validator)); return base.Build(); } } - - public class NativeWithdrawBidBuilder : TransactionV1Builder + + public class NativeChangeBidPublicKeyBuilder : TransactionV1Builder { //specific tx properties - private CLValue _validator = null; - private CLValue _amount = CLValue.U512((BigInteger)0); + [RequiredArg] + private CLValue _public_key; + [RequiredArg] + private CLValue _new_public_key; - public NativeWithdrawBidBuilder() + public NativeChangeBidPublicKeyBuilder() { _invocationTarget = TransactionV1Target.Native; - _entryPoint = TransactionV1EntryPoint.WithdrawBid; - } - - public NativeWithdrawBidBuilder Validator(PublicKey publicKey) - { - _validator = CLValue.PublicKey(publicKey); - return this; + _entryPoint = TransactionV1EntryPoint.ChangeBidPublicKey; } - public NativeWithdrawBidBuilder Amount(ulong amount) + public NativeChangeBidPublicKeyBuilder PublicKey(PublicKey publicKey) { - _amount = CLValue.U512(amount); + _public_key = CLValue.PublicKey(publicKey); return this; } - public NativeWithdrawBidBuilder Amount(BigInteger amount) + public NativeChangeBidPublicKeyBuilder NewPublicKey(PublicKey publicKey) { - _amount = CLValue.U512(amount); + _new_public_key = CLValue.PublicKey(publicKey); return this; } public override TransactionV1 Build() { + ValidateRequiredProperties(); + _runtimeArgs = new List(); - _runtimeArgs.Add(new NamedArg("public_key", _validator)); - _runtimeArgs.Add(new NamedArg("amount", _amount)); + _runtimeArgs.Add(new NamedArg("public_key", _public_key)); + _runtimeArgs.Add(new NamedArg("new_public_key", _new_public_key)); return base.Build(); } } @@ -439,7 +495,8 @@ public override TransactionV1 Build() public class NativeAddReservationsBuilder : TransactionV1Builder { //specific tx properties - private List _reservations = null; + [RequiredArg] + private List _reservations; public NativeAddReservationsBuilder() { @@ -455,6 +512,8 @@ public NativeAddReservationsBuilder Reservations(List reservations) public override TransactionV1 Build() { + ValidateRequiredProperties(); + var list = _reservations.Select(r => r.ToCLValue()); _runtimeArgs.Add(new NamedArg("reservations", CLValue.List(list.ToArray()))); return base.Build(); @@ -464,8 +523,10 @@ public override TransactionV1 Build() public class NativeCancelReservationsBuilder : TransactionV1Builder { //specific tx properties - private PublicKey _validator = null; - private List _delegators = null; + [RequiredArg] + private PublicKey _validator; + [RequiredArg] + private List _delegators; public NativeCancelReservationsBuilder() { @@ -487,9 +548,10 @@ public NativeCancelReservationsBuilder Delegators(List delegators public override TransactionV1 Build() { + ValidateRequiredProperties(); + _runtimeArgs.Add(new NamedArg("validator", CLValue.PublicKey(_validator))); - // var list = _delegators.Select(r => r.ToCLValue()); - var list = _delegators.Select(r => CLValue.PublicKey(r.PublicKey)); + var list = _delegators.Select(r => r.ToCLValue()); _runtimeArgs.Add(new NamedArg("delegators", CLValue.List(list.ToArray()))); return base.Build(); } From 569fa41d674c4baa95ea2ab9b5d663fafca7d022 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 12 Dec 2024 10:39:33 +0100 Subject: [PATCH 093/126] Rename Prepaid to Prepayment to align with PR#5010 Signed-off-by: David Hernando --- Casper.Network.SDK/Types/PricingMode.cs | 10 +++++----- Casper.Network.SDK/Types/StoredValue.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Casper.Network.SDK/Types/PricingMode.cs b/Casper.Network.SDK/Types/PricingMode.cs index 6726102..4f7ab56 100644 --- a/Casper.Network.SDK/Types/PricingMode.cs +++ b/Casper.Network.SDK/Types/PricingMode.cs @@ -31,7 +31,7 @@ public interface IPricingMode #if NET7_0_OR_GREATER public bool IsPaymentLimited => this is PaymentLimitedPricingMode; public bool IsFixed => this is FixedPricingMode; - public bool IsReserved => this is PrepaidPricingMode; + public bool IsPrepaid => this is PrepaidPricingMode; #endif public byte[] ToBytes(); @@ -120,13 +120,13 @@ public class PrepaidPricingMode : IPricingMode public string Receipt { get; init; } const ushort TAG_FIELD_INDEX = 0; - const byte RESERVED_VARIANT_TAG = 2; - const ushort RESERVED_RECEIPT_INDEX = 1; + const byte PREPAID_VARIANT_TAG = 2; + const ushort PREPAID_RECEIPT_INDEX = 1; public byte[] ToBytes() { return new CalltableSerialization() - .AddField(TAG_FIELD_INDEX, CLValue.U8(RESERVED_VARIANT_TAG)) - .AddField(RESERVED_RECEIPT_INDEX, Hex.Decode(Receipt)) + .AddField(TAG_FIELD_INDEX, CLValue.U8(PREPAID_VARIANT_TAG)) + .AddField(PREPAID_RECEIPT_INDEX, Hex.Decode(Receipt)) .GetBytes(); } } diff --git a/Casper.Network.SDK/Types/StoredValue.cs b/Casper.Network.SDK/Types/StoredValue.cs index 1425b19..d8094d5 100644 --- a/Casper.Network.SDK/Types/StoredValue.cs +++ b/Casper.Network.SDK/Types/StoredValue.cs @@ -69,7 +69,7 @@ public class StoredValue /// /// Stores location, type and data for a gas pre-payment. /// - public Prepayment Prepaid { get; init; } + public Prepayment Prepayment { get; init; } public EntryPoint EntryPoint { get; init; } From 296099267c2653c8d7be12268316e1a5d986ebca Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 17 Dec 2024 13:01:24 +0100 Subject: [PATCH 094/126] modify TransactionRuntime type and serialize stored/session targets as per new requirements for VM1. Signed-off-by: David Hernando --- Casper.Network.SDK/Types/AddressableEntity.cs | 3 +- Casper.Network.SDK/Types/StoredValue.cs | 4 +- .../Types/TransactionBuilder.cs | 39 +--- .../Types/TransactionV1EntryPoint.cs | 35 ++-- .../Types/TransactionV1Target.cs | 177 ++++++++++-------- 5 files changed, 131 insertions(+), 127 deletions(-) diff --git a/Casper.Network.SDK/Types/AddressableEntity.cs b/Casper.Network.SDK/Types/AddressableEntity.cs index bd77aed..1a73c0a 100644 --- a/Casper.Network.SDK/Types/AddressableEntity.cs +++ b/Casper.Network.SDK/Types/AddressableEntity.cs @@ -75,9 +75,10 @@ public override EntityKind Read( }; break; case "SmartContract": + var tag = reader.GetString(); entity = new EntityKind() { - SmartContract = EnumCompat.Parse(reader.GetString()), + SmartContract = TransactionRuntime.FromString(tag), }; break; case "System": diff --git a/Casper.Network.SDK/Types/StoredValue.cs b/Casper.Network.SDK/Types/StoredValue.cs index d8094d5..e1afa70 100644 --- a/Casper.Network.SDK/Types/StoredValue.cs +++ b/Casper.Network.SDK/Types/StoredValue.cs @@ -44,7 +44,7 @@ public class StoredValue /// /// Stores a package. /// - public Package Package { get; init; } + public Package SmartContract { get; init; } /// /// A record of byte code. @@ -76,7 +76,7 @@ public class StoredValue /// /// Raw bytes. Similar to a [`crate::StoredValue::CLValue`] but does not incur overhead of a [`crate::CLValue`] and [`crate::CLType`]. /// - public byte[] RawBytes { get; init; } + public string RawBytes { get; init; } public class StoredValueConverter : JsonConverter { diff --git a/Casper.Network.SDK/Types/TransactionBuilder.cs b/Casper.Network.SDK/Types/TransactionBuilder.cs index 026bdb5..8ce07cb 100644 --- a/Casper.Network.SDK/Types/TransactionBuilder.cs +++ b/Casper.Network.SDK/Types/TransactionBuilder.cs @@ -559,41 +559,31 @@ public override TransactionV1 Build() public class ContractCallBuilder : TransactionV1Builder { - private ulong _transferredValue = 0; - public ContractCallBuilder() { } public ContractCallBuilder ByHash(string contractHash) { - _invocationTarget = TransactionV1Target.StoredByHash(contractHash, _transferredValue); + _invocationTarget = TransactionV1Target.StoredByHash(contractHash); return this; } public ContractCallBuilder ByName(string name) { - _invocationTarget = TransactionV1Target.StoredByName(name, _transferredValue); + _invocationTarget = TransactionV1Target.StoredByName(name); return this; } public ContractCallBuilder ByPackageHash(string contractHash, UInt32? version = null) { - _invocationTarget = TransactionV1Target.StoredByPackageHash(contractHash, version, _transferredValue); + _invocationTarget = TransactionV1Target.StoredByPackageHash(contractHash, version); return this; } public ContractCallBuilder ByPackageName(string name, UInt32? version = null) { - _invocationTarget = TransactionV1Target.StoredByPackageName(name, version, _transferredValue); - return this; - } - - public ContractCallBuilder TransferredValue(ulong transferredValue) - { - _transferredValue = transferredValue; - if (_invocationTarget is StoredTransactionV1Target storedTransactionV1Target) - storedTransactionV1Target.TransferredValue = transferredValue; + _invocationTarget = TransactionV1Target.StoredByPackageName(name, version); return this; } @@ -613,8 +603,6 @@ public ContractCallBuilder RuntimeArgs(List args) public class SessionBuilder : TransactionV1Builder { private bool _isInstallOrUpgrade = false; - private ulong _transferredValue = 0; - private string _seed = null; private byte[] _wasm = null; public SessionBuilder() @@ -624,9 +612,8 @@ public SessionBuilder() public SessionBuilder Wasm(byte[] wasmBytes) { - var target = TransactionV1Target.Session(wasmBytes, _transferredValue); + var target = TransactionV1Target.Session(wasmBytes); target.IsInstallUpgrade = _isInstallOrUpgrade; - target.Seed = _seed; _invocationTarget = target; return this; } @@ -639,22 +626,6 @@ public SessionBuilder InstallOrUpgrade() return this; } - public SessionBuilder TransferredValue(ulong transferredValue) - { - _transferredValue = transferredValue; - if (_invocationTarget is SessionTransactionV1Target sessionTarget) - sessionTarget.TransferredValue = transferredValue; - return this; - } - - public SessionBuilder Seed(string seed) - { - _seed = seed; - if (_invocationTarget is SessionTransactionV1Target sessionTarget) - sessionTarget.Seed = seed; - return this; - } - public SessionBuilder RuntimeArgs(List args) { _runtimeArgs = args; diff --git a/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs b/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs index dbd25c6..50d5aa5 100644 --- a/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs +++ b/Casper.Network.SDK/Types/TransactionV1EntryPoint.cs @@ -10,51 +10,56 @@ namespace Casper.Network.SDK.Types /// public enum NativeEntryPoint { + /// + /// Used to call entry point call() in session transactions. + /// + Call = 0, + + /// + /// A non-native, arbitrary entry point. + /// + Custom = 1, + /// /// The `transfer` native entry point, used to transfer `Motes` from a source purse to a target purse. /// - Transfer = 1, + Transfer = 2, /// /// The `add_bid` native entry point, used to create or top off a bid purse. /// - AddBid = 2, + AddBid = 3, /// /// The `withdraw_bid` native entry point, used to decrease a stake. /// - WithdrawBid = 3, + WithdrawBid = 4, /// /// The `delegate` native entry point, used to add a new delegator or increase an existing delegator's stake. /// - Delegate = 4, + Delegate = 5, /// /// The `undelegate` native entry point, used to reduce a delegator's stake or remove the delegator if the remaining stake is 0. /// - Undelegate = 5, + Undelegate = 6, /// /// The `redelegate` native entry point, used to reduce a delegator's stake or remove the delegator if /// the remaining stake is 0, and after the unbonding delay, automatically delegate to a new validator. /// - Redelegate = 6, + Redelegate = 7, /// /// The `activate_bid` native entry point, used to used to reactivate an inactive bid. /// - ActivateBid = 7, + ActivateBid = 8, /// /// The `change_bid_public_key` native entry point, used to change a bid's public key. /// - ChangeBidPublicKey = 8, - - /// - /// Used to call entry point call() in session transactions - /// - Call = 9, + ChangeBidPublicKey = 9, /// /// The `add_reservations` native entry point, used to add delegators to validator's reserve list. @@ -103,7 +108,8 @@ public NativeTransactionV1EntryPoint(string name) } const ushort TAG_FIELD_INDEX = 0; - const byte CALL_VARIANT_TAG = 1; + const byte CALL_VARIANT_TAG = 0; + const byte CUSTOM_VARIANT_TAG = 1; const byte TRANSFER_VARIANT_TAG = 2; const byte ADD_BID_VARIANT_TAG = 3; const byte WITHDRAW_BID_VARIANT_TAG = 4; @@ -120,6 +126,7 @@ public byte[] ToBytes() var tag = Type switch { NativeEntryPoint.Call => CALL_VARIANT_TAG, + NativeEntryPoint.Custom => CUSTOM_VARIANT_TAG, NativeEntryPoint.Transfer => TRANSFER_VARIANT_TAG, NativeEntryPoint.AddBid => ADD_BID_VARIANT_TAG, NativeEntryPoint.Delegate => DELEGATE_VARIANT_TAG, diff --git a/Casper.Network.SDK/Types/TransactionV1Target.cs b/Casper.Network.SDK/Types/TransactionV1Target.cs index 1dbc990..ce6ff13 100644 --- a/Casper.Network.SDK/Types/TransactionV1Target.cs +++ b/Casper.Network.SDK/Types/TransactionV1Target.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Text.Json; using System.Text.Json.Serialization; using Casper.Network.SDK.ByteSerializers; @@ -22,7 +23,7 @@ public class ByHashInvocationTarget : IInvocationTarget const ushort TAG_FIELD_INDEX = 0; const byte BY_HASH_VARIANT = 0; const ushort BY_HASH_HASH_INDEX = 1; - + public byte[] ToBytes() { return new CalltableSerialization() @@ -35,11 +36,11 @@ public byte[] ToBytes() public class ByNameInvocationTarget : IInvocationTarget { public string Name { get; init; } - + const ushort TAG_FIELD_INDEX = 0; const byte BY_NAME_VARIANT = 1; const ushort BY_NAME_NAME_INDEX = 1; - + public byte[] ToBytes() { return new CalltableSerialization() @@ -54,12 +55,12 @@ public class ByPackageHashInvocationTarget : IInvocationTarget [JsonPropertyName("addr")] public string Hash { get; init; } [JsonPropertyName("version")] public UInt32? Version { get; init; } - + const ushort TAG_FIELD_INDEX = 0; const byte BY_PACKAGE_HASH_VARIANT = 2; const ushort BY_PACKAGE_HASH_ADDR_INDEX = 1; const ushort BY_PACKAGE_HASH_VERSION_INDEX = 2; - + public byte[] ToBytes() { return new CalltableSerialization() @@ -77,12 +78,12 @@ public class ByPackageNameInvocationTarget : IInvocationTarget [JsonPropertyName("name")] public string Name { get; init; } [JsonPropertyName("version")] public UInt32? Version { get; init; } - + const ushort TAG_FIELD_INDEX = 0; const byte BY_PACKAGE_NAME_VARIANT = 3; const ushort BY_PACKAGE_NAME_NAME_INDEX = 1; const ushort BY_PACKAGE_NAME_VERSION_INDEX = 2; - + public byte[] ToBytes() { return new CalltableSerialization() @@ -178,17 +179,62 @@ public enum TransactionTargetType Session = 2, } - public enum TransactionRuntime + public class TransactionRuntime { + private const byte VM_CASPER_V1_TAG = 0; + private const byte VM_CASPER_V2_TAG = 1; + private byte _tag = VM_CASPER_V1_TAG; + + public static TransactionRuntime FromString(string json) + { + switch (json) + { + case "VmCasperV1": + return VmCasperV1(); + case "VmCasperV2": + return VmCasperV2(); + default: + throw new JsonException($"Unknown TransactionRuntime '{json}'"); + } + } + + public override string ToString() + { + switch (_tag) + { + case VM_CASPER_V1_TAG: + return "VmCasperV1"; + case VM_CASPER_V2_TAG: + return "VmCasperV2"; + default: + throw new JsonException($"Unknown TransactionRuntime '{_tag}'"); + } + } + /// /// The Casper Version 1 Virtual Machine. /// - VmCasperV1, + public static TransactionRuntime VmCasperV1() + { + return new TransactionRuntime() { _tag = VM_CASPER_V1_TAG }; + } /// /// The Casper Version 2 Virtual Machine. /// - VmCasperV2, + public static TransactionRuntime VmCasperV2() + { + return new TransactionRuntime() { _tag = VM_CASPER_V2_TAG }; + } + + const ushort TAG_FIELD_INDEX = 0; + + public byte[] ToBytes() + { + return new CalltableSerialization() + .AddField(TAG_FIELD_INDEX, new byte[] { _tag }) + .GetBytes(); + } } public interface ITransactionV1Target @@ -200,7 +246,7 @@ public class NativeTransactionV1Target : ITransactionV1Target { const ushort TAG_FIELD_INDEX = 0; const byte NATIVE_VARIANT = 0; - + public byte[] ToBytes() { return new CalltableSerialization() @@ -219,25 +265,23 @@ public class StoredTransactionV1Target : ITransactionV1Target /// [JsonPropertyName("runtime")] public TransactionRuntime Runtime { get; set; } - - /// - /// The amount of motes to transfer before code is executed. - /// - [JsonPropertyName("transferred_value")] - public ulong TransferredValue { get; set; } - + + public StoredTransactionV1Target() + { + Runtime = TransactionRuntime.VmCasperV1(); + } + const ushort TAG_FIELD_INDEX = 0; const byte STORED_VARIANT = 1; const ushort STORED_ID_INDEX = 1; const ushort STORED_RUNTIME_INDEX = 2; - const ushort STORED_TRANSFERRED_VALUE_INDEX = 3; + public byte[] ToBytes() { return new CalltableSerialization() .AddField(TAG_FIELD_INDEX, new byte[] { STORED_VARIANT }) .AddField(STORED_ID_INDEX, Id.ToBytes()) - .AddField(STORED_RUNTIME_INDEX, new byte[] { (byte)Runtime}) - .AddField(STORED_TRANSFERRED_VALUE_INDEX, CLValue.U64(TransferredValue)) + .AddField(STORED_RUNTIME_INDEX, Runtime.ToBytes()) .GetBytes(); } } @@ -249,104 +293,90 @@ public class SessionTransactionV1Target : ITransactionV1Target /// [JsonPropertyName("is_install_upgrade")] public bool IsInstallUpgrade { get; set; } - + /// /// Wasm bytes for a Session transaction type. /// [JsonPropertyName("module_bytes")] [JsonConverter(typeof(HexBytesConverter))] public byte[] ModuleBytes { get; set; } - + /// /// Targeted Casper VM version. /// [JsonPropertyName("runtime")] public TransactionRuntime Runtime { get; set; } - - /// - /// The amount of motes to transfer before code is executed. - /// This is for protection against phishing attack where a malicious session code drains - /// the balance of the caller account. The amount stated here is the maximum amount - /// that can be transferred from the caller account to the session account. - /// - [JsonPropertyName("transferred_value")] - public ulong TransferredValue { get; set; } - - /// - /// The seed for the session code that is used for an installer. - /// - [JsonPropertyName("seed")] - public string Seed { get; set; } - + + public SessionTransactionV1Target() + { + Runtime = TransactionRuntime.VmCasperV1(); + } + const ushort TAG_FIELD_INDEX = 0; const byte SESSION_VARIANT = 2; const ushort SESSION_IS_INSTALL_INDEX = 1; const ushort SESSION_RUNTIME_INDEX = 2; const ushort SESSION_MODULE_BYTES_INDEX = 3; - const ushort SESSION_TRANSFERRED_VALUE_INDEX = 4; - const ushort SESSION_SEED_INDEX = 5; + public byte[] ToBytes() { + var ms = new MemoryStream(); + ms.Write(BitConverter.GetBytes(ModuleBytes.Length)); + ms.Write(ModuleBytes); + return new CalltableSerialization() .AddField(TAG_FIELD_INDEX, new byte[] { SESSION_VARIANT }) - .AddField(SESSION_IS_INSTALL_INDEX, new byte[] { IsInstallUpgrade ? (byte)0x01 :(byte) 0x00 }) - .AddField(SESSION_RUNTIME_INDEX, new byte[] { (byte)Runtime }) - .AddField(SESSION_MODULE_BYTES_INDEX, ModuleBytes) - .AddField(SESSION_TRANSFERRED_VALUE_INDEX, CLValue.U64(TransferredValue)) - .AddField(SESSION_SEED_INDEX, Hex.Decode(Seed)) + .AddField(SESSION_IS_INSTALL_INDEX, new byte[] { IsInstallUpgrade ? (byte)0x01 : (byte)0x00 }) + .AddField(SESSION_RUNTIME_INDEX, Runtime.ToBytes()) + .AddField(SESSION_MODULE_BYTES_INDEX, ms.ToArray()) .GetBytes(); } } - + public class TransactionV1Target { public static ITransactionV1Target Native => new NativeTransactionV1Target(); - - public static StoredTransactionV1Target StoredByHash(string contractHash, ulong transferredValue = 0) + + public static StoredTransactionV1Target StoredByHash(string contractHash) { return new StoredTransactionV1Target() { Id = new ByHashInvocationTarget { Hash = contractHash }, - TransferredValue = transferredValue, }; } - public static StoredTransactionV1Target StoredByName(string name, ulong transferredValue = 0) + public static StoredTransactionV1Target StoredByName(string name) { return new StoredTransactionV1Target() { Id = new ByNameInvocationTarget { Name = name }, - TransferredValue = transferredValue, }; } - public static StoredTransactionV1Target StoredByPackageHash(string packageHash, UInt32? version = null, ulong transferredValue = 0) + public static StoredTransactionV1Target StoredByPackageHash(string packageHash, UInt32? version = null) { return new StoredTransactionV1Target() { Id = new ByPackageHashInvocationTarget { Hash = packageHash, Version = version }, - TransferredValue = transferredValue, }; } - public static StoredTransactionV1Target StoredByPackageName(string name, UInt32? version = null, ulong transferredValue = 0) + public static StoredTransactionV1Target StoredByPackageName(string name, UInt32? version = null) { return new StoredTransactionV1Target() { Id = new ByPackageNameInvocationTarget() { Name = name, Version = version }, - TransferredValue = transferredValue, }; } - public static SessionTransactionV1Target Session(byte[] moduleBytes, ulong transferredValue = 0) + public static SessionTransactionV1Target Session(byte[] moduleBytes) { return new SessionTransactionV1Target() { ModuleBytes = moduleBytes, - TransferredValue = transferredValue, }; } - + public class TransactionTargetConverter : JsonConverter { public override ITransactionV1Target Read( @@ -366,13 +396,14 @@ public override ITransactionV1Target Read( throw new JsonException($"TransactionTargetType '{targetType}' not supported."); } } + if (reader.TokenType == JsonTokenType.StartObject) { ITransactionV1Target transactionTarget = null; IInvocationTarget id = null; string module_bytes = null; - ulong transferredValue = 0;; - TransactionRuntime runtime = TransactionRuntime.VmCasperV1; + bool is_install_upgrade = false; + TransactionRuntime runtime = TransactionRuntime.VmCasperV1(); reader.Read(); // skip start object var targetType = reader.GetString(); @@ -392,12 +423,8 @@ public override ITransactionV1Target Read( id = JsonSerializer.Deserialize(ref reader, options); reader.Read(); break; - case "transferred_value": - transferredValue = reader.GetUInt64(); - reader.Read(); - break; case "runtime": - runtime = EnumCompat.Parse(reader.GetString()); + runtime = TransactionRuntime.FromString(reader.GetString()); reader.Read(); // skip end object break; } @@ -408,7 +435,6 @@ public override ITransactionV1Target Read( transactionTarget = new StoredTransactionV1Target() { Id = id, - TransferredValue = transferredValue, Runtime = runtime, }; break; @@ -420,15 +446,17 @@ public override ITransactionV1Target Read( reader.Read(); switch (prop) { + case "is_install_upgrade": + is_install_upgrade = reader.GetBoolean(); + reader.Read(); + break; case "module_bytes": module_bytes = reader.GetString(); - break; - case "transferred_value": - transferredValue = reader.GetUInt64(); reader.Read(); break; case "runtime": - runtime = EnumCompat.Parse(reader.GetString()); + runtime = TransactionRuntime.FromString(reader.GetString()); + reader.Read(); break; } } @@ -437,8 +465,8 @@ public override ITransactionV1Target Read( transactionTarget = new SessionTransactionV1Target() { + IsInstallUpgrade = is_install_upgrade, ModuleBytes = Hex.Decode(module_bytes), - TransferredValue = transferredValue, Runtime = runtime, }; break; @@ -467,8 +495,6 @@ public override void Write( writer.WriteStartObject("Stored"); writer.WritePropertyName("id"); JsonSerializer.Serialize(writer, storedTarget.Id); - writer.WritePropertyName("transferred_value"); - writer.WriteNumberValue(storedTarget.TransferredValue); writer.WriteString("runtime", storedTarget.Runtime.ToString()); writer.WriteEndObject(); writer.WriteEndObject(); @@ -476,9 +502,8 @@ public override void Write( case SessionTransactionV1Target sessionTarget: writer.WriteStartObject(); writer.WriteStartObject("Session"); + writer.WriteBoolean("is_install_upgrade", sessionTarget.IsInstallUpgrade); writer.WriteString("module_bytes", Hex.ToHexString(sessionTarget.ModuleBytes)); - writer.WritePropertyName("transferred_value"); - writer.WriteNumberValue(sessionTarget.TransferredValue); writer.WriteString("runtime", sessionTarget.Runtime.ToString()); writer.WriteEndObject(); writer.WriteEndObject(); From aa797ee1cfec4b35872a1ab89509b4943f54c6ec Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 18 Dec 2024 09:49:34 +0100 Subject: [PATCH 095/126] CSDK-195 Signed-off-by: David Hernando --- Casper.Network.SDK/Types/AddressableEntity.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Casper.Network.SDK/Types/AddressableEntity.cs b/Casper.Network.SDK/Types/AddressableEntity.cs index bd77aed..3f4db65 100644 --- a/Casper.Network.SDK/Types/AddressableEntity.cs +++ b/Casper.Network.SDK/Types/AddressableEntity.cs @@ -167,11 +167,5 @@ public class AddressableEntity /// [JsonPropertyName("action_thresholds")] public ActionThresholds ActionThresholds { get; init; } - - /// - /// Message topic list for this entity - /// - [JsonPropertyName("message_topics")] - public List MessageTopics { get; init; } } } \ No newline at end of file From dbde986252dafced41b65f941fc6fe00fa14faae Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 18 Dec 2024 10:02:23 +0100 Subject: [PATCH 096/126] update test data Signed-off-by: David Hernando --- .../TestData/get-entity-account-v200.json | 3 +-- .../TestData/get-entity-contract-v200.json | 8 +------- .../TestData/get-transaction-session-v200.json | 12 +----------- 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/Casper.Network.SDK.Test/TestData/get-entity-account-v200.json b/Casper.Network.SDK.Test/TestData/get-entity-account-v200.json index 3bc214e..8e080fa 100644 --- a/Casper.Network.SDK.Test/TestData/get-entity-account-v200.json +++ b/Casper.Network.SDK.Test/TestData/get-entity-account-v200.json @@ -20,8 +20,7 @@ "deployment": 1, "upgrade_management": 1, "key_management": 1 - }, - "message_topics": [] + } }, "named_keys": [], "entry_points": [] diff --git a/Casper.Network.SDK.Test/TestData/get-entity-contract-v200.json b/Casper.Network.SDK.Test/TestData/get-entity-contract-v200.json index 2420021..584f4af 100644 --- a/Casper.Network.SDK.Test/TestData/get-entity-contract-v200.json +++ b/Casper.Network.SDK.Test/TestData/get-entity-contract-v200.json @@ -20,13 +20,7 @@ "deployment": 1, "upgrade_management": 1, "key_management": 1 - }, - "message_topics": [ - { - "topic_name": "events", - "topic_name_hash": "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1" - } - ] + } }, "named_keys": [ { diff --git a/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json b/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json index e8e323b..d0bb3bb 100644 --- a/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json +++ b/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json @@ -889,17 +889,7 @@ "deployment": 1, "upgrade_management": 1, "key_management": 1 - }, - "message_topics": [ - { - "topic_name": "errors", - "topic_name_hash": "b38b3a8f7a7cb169b9869f1b660e328df63941f4f078d284a0058140375ec7fc" - }, - { - "topic_name": "events", - "topic_name_hash": "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1" - } - ] + } } } } From 41531815e6cd8581659e695802281825e80bd294 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 18 Dec 2024 10:43:42 +0100 Subject: [PATCH 097/126] CSDK-195 Update MessageKey parsing. Signed-off-by: David Hernando --- .../Types/GlobalStateKey/MessageKey.cs | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs index 2e14080..0c42400 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs @@ -10,7 +10,7 @@ public class MessageKey : GlobalStateKey private const string KEY_PREFIX = "message-"; private const string TOPIC_PREFIX = "topic-"; - public AddressableEntityKey AddressableEntity { get; init; } + public string HashAddr { get; init; } public string TopicHash { get; init; } @@ -28,24 +28,28 @@ public MessageKey(string key) : base(key) { key = key.Substring(TOPIC_PREFIX.Length); var parts = key.Split('-'); - if (parts.Length != 4) - throw new Exception("Key not valid. It should have an entity address and a topic hash."); - - AddressableEntity = new AddressableEntityKey($"{parts[0]}-{parts[1]}-{parts[2]}"); - TopicHash = parts[3]; + if(parts.Length == 2) + { + HashAddr = parts[0]; + TopicHash = parts[1]; + } + else + throw new Exception("Key not valid. It should have a hash address and a topic hash."); } else { var parts = key.Split('-'); - if (parts.Length != 5) - throw new Exception("Key not valid. It should have an entity address, a topic hash, and a message index."); - - AddressableEntity = new AddressableEntityKey($"{parts[0]}-{parts[1]}-{parts[2]}"); - TopicHash = parts[3]; - - if(parts[4].Length == 0) - throw new Exception("Key not valid. Expected a non-empty message index."); - Index = Convert.ToUInt32(parts[4], 16); + if (parts.Length == 3) + { + HashAddr = parts[0]; + TopicHash = parts[1]; + + if(parts[2].Length == 0) + throw new Exception("Key not valid. Expected a non-empty message index."); + Index = Convert.ToUInt32(parts[2], 16); + } + else + throw new Exception("Key not valid. It should have a hash address, a topic hash, and a message index."); } } @@ -56,7 +60,8 @@ public MessageKey(byte[] key) : base(null) var ms = new MemoryStream(key); var reader = new BinaryReader(ms); - AddressableEntity = new AddressableEntityKey(reader); + var hash = reader.ReadBytes(32); + HashAddr = Hex.ToHexString(hash); var topic = reader.ReadBytes(32); TopicHash = Hex.ToHexString(topic); @@ -67,7 +72,7 @@ public MessageKey(byte[] key) : base(null) Key = KEY_PREFIX + (Index.HasValue ? "" : TOPIC_PREFIX) + - AddressableEntity.ToString() + "-" + + HashAddr + "-" + TopicHash + (Index.HasValue ? "-" + Index.Value.ToString("x") : ""); } From d144382d083d6ccc2894b296155bc1c46995ea29 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 18 Dec 2024 11:07:23 +0100 Subject: [PATCH 098/126] fix GlobalSTateKey tests Signed-off-by: David Hernando --- Casper.Network.SDK.Test/GlobalStateKeyTest.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Casper.Network.SDK.Test/GlobalStateKeyTest.cs b/Casper.Network.SDK.Test/GlobalStateKeyTest.cs index 32aeda5..e05e781 100644 --- a/Casper.Network.SDK.Test/GlobalStateKeyTest.cs +++ b/Casper.Network.SDK.Test/GlobalStateKeyTest.cs @@ -539,9 +539,9 @@ public void BalanceHoldKeyTest() [Test] public void MessageTopicKeyTest() { - var entityKeyStr = "entity-contract-55d4a6915291da12afded37fa5bc01f0803a2f0faf6acb7ec4c7ca6ab76f3330"; + var hashAddr = "55d4a6915291da12afded37fa5bc01f0803a2f0faf6acb7ec4c7ca6ab76f3330"; var topicStr = "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1"; - var msgKeyStr = $"message-topic-{entityKeyStr}-{topicStr}"; + var msgKeyStr = $"message-topic-{hashAddr}-{topicStr}"; var key = GlobalStateKey.FromString(msgKeyStr); Assert.IsNotNull(key); @@ -549,8 +549,8 @@ public void MessageTopicKeyTest() var messageKey = key as MessageKey; Assert.IsNotNull(messageKey); - Assert.IsNotNull(messageKey.AddressableEntity); - Assert.AreEqual(entityKeyStr, messageKey.AddressableEntity.ToString().ToLower()); + Assert.IsNotNull(messageKey.HashAddr); + Assert.AreEqual(hashAddr, messageKey.HashAddr); Assert.AreEqual(topicStr, messageKey.TopicHash); Assert.IsFalse(messageKey.Index.HasValue); Assert.AreEqual(msgKeyStr, messageKey.ToString().ToLower()); @@ -560,10 +560,10 @@ public void MessageTopicKeyTest() [Test] public void MessageIndexKeyTest() { - var entityKeyStr = "entity-contract-55d4a6915291da12afded37fa5bc01f0803a2f0faf6acb7ec4c7ca6ab76f3330"; + var hashAddr = "55d4a6915291da12afded37fa5bc01f0803a2f0faf6acb7ec4c7ca6ab76f3330"; var topicStr = "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1"; var indexStr = "f"; - var msgKeyStr = $"message-{entityKeyStr}-{topicStr}-{indexStr}"; + var msgKeyStr = $"message-{hashAddr}-{topicStr}-{indexStr}"; var key = GlobalStateKey.FromString(msgKeyStr); Assert.IsNotNull(key); @@ -571,8 +571,8 @@ public void MessageIndexKeyTest() var messageKey = key as MessageKey; Assert.IsNotNull(messageKey); - Assert.IsNotNull(messageKey.AddressableEntity); - Assert.AreEqual(entityKeyStr, messageKey.AddressableEntity.ToString().ToLower()); + Assert.IsNotNull(messageKey.HashAddr); + Assert.AreEqual(hashAddr, messageKey.HashAddr); Assert.AreEqual(topicStr, messageKey.TopicHash); Assert.IsTrue(messageKey.Index.HasValue); Assert.AreEqual(15, messageKey.Index.Value); @@ -592,7 +592,7 @@ public void BidAddrKeyTest() var bidAddrKey = key as BidAddrKey; Assert.AreEqual(BidAddrTag.Unified, bidAddrKey.Tag); Assert.AreEqual("account-hash-2f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c458007309", bidAddrKey.Unified.ToString().ToLower()); - Assert.IsNull(bidAddrKey.Delegator); + Assert.IsNull(bidAddrKey.DelegatorAccount); Assert.AreEqual(0, bidAddrKey.EraId); } @@ -605,7 +605,7 @@ public void BidAddrKeyTest() var bidAddrKey = key as BidAddrKey; Assert.AreEqual(BidAddrTag.Validator, bidAddrKey.Tag); Assert.AreEqual("account-hash-2f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c458007309", bidAddrKey.Validator.ToString().ToLower()); - Assert.IsNull(bidAddrKey.Delegator); + Assert.IsNull(bidAddrKey.DelegatorAccount); Assert.AreEqual(0, bidAddrKey.EraId); } { @@ -615,9 +615,9 @@ public void BidAddrKeyTest() Assert.AreEqual(KeyIdentifier.BidAddr, key.KeyIdentifier); var bidAddrKey = key as BidAddrKey; - Assert.AreEqual(BidAddrTag.Delegator, bidAddrKey.Tag); + Assert.AreEqual(BidAddrTag.DelegatedAccount, bidAddrKey.Tag); Assert.AreEqual("account-hash-2f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c458007309", bidAddrKey.Validator.ToString().ToLower()); - Assert.AreEqual("account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4", bidAddrKey.Delegator.ToString().ToLower()); + Assert.AreEqual("account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4", bidAddrKey.DelegatorAccount.ToString().ToLower()); Assert.AreEqual(0, bidAddrKey.EraId); } @@ -631,7 +631,7 @@ public void BidAddrKeyTest() var bidAddrKey = key as BidAddrKey; Assert.AreEqual(BidAddrTag.Credit, bidAddrKey.Tag); Assert.AreEqual("account-hash-520037cd249ccbcfeb0b9feae07d8d4f7d922cf88adc4f3e8691f9d34ccc8d09", bidAddrKey.Validator.ToString().ToLower()); - Assert.IsNull(bidAddrKey.Delegator); + Assert.IsNull(bidAddrKey.DelegatorAccount); Assert.AreEqual(127, bidAddrKey.EraId); } } From 4ed286abb870876ebe440e6713a8075f493206bc Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 18 Dec 2024 11:31:52 +0100 Subject: [PATCH 099/126] Remove Category from Transaction abstract type Fix tests. Signed-off-by: David Hernando --- .../RPCResponses/GetEntityResultTest.cs | 2 +- .../RPCResponses/GetTransactionTest.cs | 13 +- .../get-transaction-deploy-stored-v200.json | 14 +- .../get-transaction-session-v200.json | 1584 ++++++----------- .../TestData/get-transaction-stored-v200.json | 286 +-- Casper.Network.SDK/Types/StoredValue.cs | 13 +- Casper.Network.SDK/Types/Transaction.cs | 7 - Docs/Examples/NativeTransfer/Program.cs | 2 +- 8 files changed, 583 insertions(+), 1338 deletions(-) diff --git a/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs index b11ed89..6b04ddc 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs @@ -45,7 +45,7 @@ public void GetEntityContractTest_v200() Assert.IsNotNull(result.Entity.Package); Assert.IsNotNull(result.Entity.MainPurse); Assert.AreEqual("byte-code-85def61e3ee02e10a1e845cfb8e8b2d9640a18f605333158027a24ed8569d895", result.Entity.ByteCodeHash); - Assert.AreEqual(TransactionRuntime.VmCasperV1, result.Entity.EntityKind.SmartContract); + Assert.AreEqual(TransactionRuntime.VmCasperV1().ToString(), result.Entity.EntityKind.SmartContract); Assert.AreEqual(1, result.Entity.ActionThresholds.KeyManagement); Assert.AreEqual(1, result.Entity.ActionThresholds.Deployment); Assert.AreEqual(1, result.Entity.ActionThresholds.UpgradeManagement); diff --git a/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs index 2452f49..9983185 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs @@ -29,7 +29,6 @@ public void GetTransactionWithDeployHashTest_v200() Assert.IsNull(result.Transaction.InitiatorAddr.AccountHash); Assert.IsTrue(result.Transaction.Invocation is Transaction.NativeTransactionInvocation); Assert.AreEqual(NativeEntryPoint.Transfer, (result.Transaction.Invocation as Transaction.NativeTransactionInvocation)!.Type); - Assert.AreEqual(TransactionCategory.Mint, result.Transaction.Category); Assert.IsTrue(result.Transaction.Scheduling is StandardTransactionScheduling); Assert.AreEqual(deploy.Approvals.Count, result.Transaction.Approvals.Count); Assert.AreEqual(deploy.Approvals[0].Signature, result.Transaction.Approvals[0].Signature); @@ -63,7 +62,6 @@ public void GetDeployWithDeployHashTest_v200() Assert.IsNull(transaction.InitiatorAddr.AccountHash); Assert.IsTrue(transaction.Invocation is Transaction.NativeTransactionInvocation); Assert.AreEqual(NativeEntryPoint.Transfer, (transaction.Invocation as Transaction.NativeTransactionInvocation)!.Type); - Assert.AreEqual(TransactionCategory.Mint, transaction.Category); Assert.IsTrue(transaction.Scheduling is StandardTransactionScheduling); Assert.AreEqual(deploy.Approvals.Count, transaction.Approvals.Count); Assert.AreEqual(deploy.Approvals[0].Signature, transaction.Approvals[0].Signature); @@ -96,10 +94,9 @@ public void GetTransactionWithSessionTransactionTest_v200() Assert.IsNull(transaction.InitiatorAddr.AccountHash); Assert.IsTrue(transaction.Invocation is Transaction.SessionTransactionInvocation); Assert.AreEqual("01020304", Hex.ToHexString((transaction.Invocation as Transaction.SessionTransactionInvocation)!.Wasm)); - Assert.AreEqual(TransactionCategory.InstallUpgrade, transaction.Category); Assert.IsTrue(transaction.Scheduling is StandardTransactionScheduling); - Assert.IsTrue(transaction.PricingMode is FixedPricingMode); - Assert.AreEqual(2, ((FixedPricingMode)transaction.PricingMode).GasPriceTolerance); + Assert.IsTrue(transaction.PricingMode is PaymentLimitedPricingMode); + Assert.AreEqual(1, ((PaymentLimitedPricingMode)transaction.PricingMode).GasPriceTolerance); Assert.AreEqual(transactionV1.Approvals.Count, transaction.Approvals.Count); Assert.AreEqual(transactionV1.Approvals[0].Signature, transaction.Approvals[0].Signature); @@ -128,11 +125,10 @@ public void GetTransactionWithStoredTransactionTest_v200() Assert.AreEqual(transactionV1.Payload.ChainName, transaction.ChainName); Assert.AreEqual(transactionV1.Payload.Timestamp, transaction.Timestamp); Assert.AreEqual(transactionV1.Payload.InitiatorAddr.AccountHash, transaction.InitiatorAddr.AccountHash); - Assert.AreEqual("account-hash-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f", transaction.InitiatorAddr.AccountHash.ToString().ToLower()); - Assert.IsNull(transaction.InitiatorAddr.PublicKey); + Assert.AreEqual("01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", transaction.InitiatorAddr.PublicKey.ToString().ToLower()); + Assert.IsNull(transaction.InitiatorAddr.AccountHash); Assert.IsTrue(transaction.Invocation is Transaction.StoredTransactionInvocation); Assert.AreEqual("transfer", (transaction.Invocation as Transaction.StoredTransactionInvocation)!.EntryPoint); - Assert.AreEqual(TransactionCategory.Medium, transaction.Category); Assert.IsTrue(transaction.Scheduling is StandardTransactionScheduling); Assert.AreEqual(transactionV1.Approvals.Count, transaction.Approvals.Count); Assert.AreEqual(transactionV1.Approvals[0].Signature, transaction.Approvals[0].Signature); @@ -167,7 +163,6 @@ public void GetTransactionWithStoredDeployTest_v200() Assert.IsNull(transaction.InitiatorAddr.AccountHash); Assert.IsTrue(transaction.Invocation is Transaction.StoredTransactionInvocation); Assert.AreEqual("transfer", (transaction.Invocation as Transaction.StoredTransactionInvocation)!.EntryPoint); - Assert.AreEqual(TransactionCategory.Large, transaction.Category); Assert.IsTrue(transaction.Scheduling is StandardTransactionScheduling); Assert.AreEqual(deploy.Approvals.Count, transaction.Approvals.Count); Assert.AreEqual(deploy.Approvals[0].Signature, transaction.Approvals[0].Signature); diff --git a/Casper.Network.SDK.Test/TestData/get-transaction-deploy-stored-v200.json b/Casper.Network.SDK.Test/TestData/get-transaction-deploy-stored-v200.json index 54766a3..404246f 100644 --- a/Casper.Network.SDK.Test/TestData/get-transaction-deploy-stored-v200.json +++ b/Casper.Network.SDK.Test/TestData/get-transaction-deploy-stored-v200.json @@ -141,19 +141,19 @@ "kind": "Identity" }, { - "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "key": "message-topic-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", "kind": "Identity" }, { - "key": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-1", + "key": "message-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-1", "kind": { - "Prune": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-1" + "Prune": "message-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-1" } }, { - "key": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-2", + "key": "message-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-2", "kind": { - "Prune": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-2" + "Prune": "message-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-2" } }, { @@ -161,7 +161,7 @@ "kind": "Identity" }, { - "key": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-0", + "key": "message-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-0", "kind": { "Write": { "Message": "message-checksum-4e8fa8422d6022df02a2d41568e25ebfbc9d7d098f8c31f42e4493523627f0f4" @@ -169,7 +169,7 @@ } }, { - "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "key": "message-topic-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", "kind": { "Write": { "MessageTopic": { diff --git a/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json b/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json index e8e323b..feeffa6 100644 --- a/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json +++ b/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json @@ -2,108 +2,110 @@ "api_version": "2.0.0", "transaction": { "Version1": { - "hash": "45a4d4ce6ff79d7b49c34d2c7bdc55543d1734b1a03d66bea513e631ff21213b", - "header": { - "chain_name": "casper-net-1", - "timestamp": "2024-07-17T08:52:03.256Z", + "hash": "22ef28d7e341c79d868e1be407ec4af4f25c267c0094201ba511045b3af191f6", + "payload": { + "initiator_addr": { + "PublicKey": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a" + }, + "timestamp": "2024-12-18T09:03:15.790Z", "ttl": "30m", - "body_hash": "9b1d4002bb1af17ec9fc4193ac5ba7db6535b6dd813034b730829a94b13ebc46", + "chain_name": "casper-net-1", "pricing_mode": { - "Fixed": { - "gas_price_tolerance": 2 + "PaymentLimited": { + "payment_amount": 1000000000000, + "gas_price_tolerance": 1, + "standard_payment": true } }, - "initiator_addr": { - "PublicKey": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4" - } - }, - "body": { - "args": [ - [ - "name", - { - "cl_type": "String", - "bytes": "0b000000434c49434b312054657374", - "parsed": "CLICK1 Test" - } - ], - [ - "symbol", - { - "cl_type": "String", - "bytes": "06000000434c49434b31", - "parsed": "CLICK1" - } - ], - [ - "decimals", - { - "cl_type": "U8", - "bytes": "09", - "parsed": 9 - } - ], - [ - "total_supply", - { - "cl_type": "U256", - "bytes": "070080c6a47e8d03", - "parsed": "1000000000000000" - } - ], - [ - "events_mode", - { - "cl_type": "U8", - "bytes": "05", - "parsed": 5 - } - ], - [ - "enable_mint_burn", - { - "cl_type": "U8", - "bytes": "01", - "parsed": 1 + "fields": { + "args": { + "Named": [ + [ + "name", + { + "cl_type": "String", + "bytes": "0b000000434c49434b542054657374", + "parsed": "CLICKT Test" + } + ], + [ + "symbol", + { + "cl_type": "String", + "bytes": "06000000434c49434b54", + "parsed": "CLICKT" + } + ], + [ + "decimals", + { + "cl_type": "U8", + "bytes": "09", + "parsed": 9 + } + ], + [ + "total_supply", + { + "cl_type": "U256", + "bytes": "070080c6a47e8d03", + "parsed": "1000000000000000" + } + ], + [ + "events_mode", + { + "cl_type": "U8", + "bytes": "02", + "parsed": 2 + } + ], + [ + "enable_mint_burn", + { + "cl_type": "U8", + "bytes": "01", + "parsed": 1 + } + ] + ] + }, + "entry_point": "Call", + "scheduling": "Standard", + "target": { + "Session": { + "is_install_upgrade": true, + "module_bytes": "01020304", + "runtime": "VmCasperV1" } - ] - ], - "target": { - "Session": { - "module_bytes": "01020304", - "runtime": "VmCasperV1" } - }, - "entry_point": "Call", - "transaction_category": 2, - "scheduling": "Standard" + } }, "approvals": [ { - "signer": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", - "signature": "016a910dafe6b87b8f12daa5698c33c0ab06eda11e79e06cacc446d3de65d85176fa26a0a9036ad80272aca8b1dfe674227e9cfaa98509e60439879d4fde487b04" + "signer": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", + "signature": "013bb82f647fcb53a4fa01871e43100bbee1a90370480361a96f426280b73c07adee7a7116b6a433f0173b8a2403d95c3fa84fbcb28f74535f6a598721d299300a" } ] } }, "execution_info": { - "block_hash": "b84c248b4c1bee3abf77e84b921fcc0d09290a1556285327922da4dc23de3177", - "block_height": 1809, + "block_hash": "856ce7684bc53fc76e27d91a9dfda2c158fd18a69789fecbdf98535d3bddadf0", + "block_height": 72, "execution_result": { "Version2": { "initiator": { - "PublicKey": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4" + "PublicKey": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a" }, "error_message": null, "limit": "1000000000000", - "consumed": "350280524938", + "consumed": "314102340989", "cost": "1000000000000", - "payment": [], "transfers": [], - "size_estimate": 303776, + "size_estimate": 278442, "effects": [ { - "key": "balance-hold-016f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee042215af3fe4bf90010000", + "key": "balance-hold-01e542fb9f5dc6d06d849e2ac3ad6cc5cd4b7c71392dc8759c0898f4ab04439520bee001d993010000", "kind": { "Write": { "CLValue": { @@ -115,31 +117,31 @@ } }, { - "key": "uref-5aa675065318455e193fc341bb4750174283add94ca195f71e4b4104e4c84b1a-000", + "key": "uref-6702ca24ad114bf8fe2e7e2169461b98e11f12ad7ad191d6e4b6594183f64ecf-000", "kind": { "Write": { "CLValue": { "cl_type": "String", - "bytes": "0b000000434c49434b312054657374", - "parsed": "CLICK1 Test" + "bytes": "0b000000434c49434b542054657374", + "parsed": "CLICKT Test" } } } }, { - "key": "uref-977f30046644e5c7a7e8cb297e5f8971a1680543dc31d1066ab4870c575b8a0d-000", + "key": "uref-644d8cce1ad2d0fb9a783a72e9542a4edc3e88e62f13e55d3d6a2899cd205619-000", "kind": { "Write": { "CLValue": { "cl_type": "String", - "bytes": "06000000434c49434b31", - "parsed": "CLICK1" + "bytes": "06000000434c49434b54", + "parsed": "CLICKT" } } } }, { - "key": "uref-63c283118dd23b4ceb6fb4eabe219941ca795a9588f5e09c1438cf7e4904d285-000", + "key": "uref-127a8c723a6df941dae0d26a9c181c26f1a80f86eaeb12eed786b4c9715b5b55-000", "kind": { "Write": { "CLValue": { @@ -151,7 +153,7 @@ } }, { - "key": "uref-5b2eb4a8703d23be50b30625811388876b10ea57c6f79e613a144b59bc8eea1d-000", + "key": "uref-411a53953e73ddd23a115ad96e7ccb53e1867ec4beec03e62b8c7aa400be56fe-000", "kind": { "Write": { "CLValue": { @@ -163,19 +165,19 @@ } }, { - "key": "uref-08f9c3e110c4d5cbc6e8ce71dd0cd535e4f41b34fe88c26cf3cd222004373db3-000", + "key": "uref-a0cb023abb00bf87f9898b5fdce36569d5b87b7891e2b5023e663a5bc78134e0-000", "kind": { "Write": { "CLValue": { "cl_type": "U8", - "bytes": "05", - "parsed": 5 + "bytes": "02", + "parsed": 2 } } } }, { - "key": "uref-ed2804ba22d80b1cc8abb02f72e1d87e93b3186d1225cf286b733e29c4df61e8-000", + "key": "uref-c09b022180e6a9221dfefe6ae62c6c8a93e09031742f284ffb64c5c1ddd23395-000", "kind": { "Write": { "CLValue": { @@ -187,7 +189,7 @@ } }, { - "key": "uref-5ef62926d2a73b5982cab9cf546b59e75cd72b1c57c5eaaf2a01c90705fb6b9b-000", + "key": "uref-d8a4e0ed98096e0df3263828e46c1f6758255853d3abcb32ebaf0776483a46bb-000", "kind": { "Write": { "CLValue": { @@ -199,10 +201,11 @@ } }, { - "key": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", + "key": "hash-976b060d3ae59125f3521c5dd16ca5f55c9862b37a9effa05c3770a9a4d7b870", "kind": { "Write": { - "Package": { + "ContractPackage": { + "access_key": "uref-d8a4e0ed98096e0df3263828e46c1f6758255853d3abcb32ebaf0776483a46bb-007", "versions": [], "disabled_versions": [], "groups": [], @@ -212,710 +215,390 @@ } }, { - "key": "named-key-entity-account-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f-fc71cc1c9046f7ac32c20c9f9897c6c3a0b6bb288e9e54eb29202867ca9b0d94", + "key": "account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4", "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "10ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", - "parsed": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4" - }, - "name": { - "cl_type": "String", - "bytes": "2200000063657031385f636f6e74726163745f7061636b6167655f434c49434b312054657374", - "parsed": "cep18_contract_package_CLICK1 Test" - } + "AddKeys": [ + { + "name": "cep18_contract_package_CLICKT Test", + "key": "package-976b060d3ae59125f3521c5dd16ca5f55c9862b37a9effa05c3770a9a4d7b870" } - } + ] } }, { - "key": "named-key-entity-account-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f-715bf96dd4d151db8e1135387b051ea188d8fc85ca004740d4f181c6ad038731", + "key": "account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4", "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "025ef62926d2a73b5982cab9cf546b59e75cd72b1c57c5eaaf2a01c90705fb6b9b07", - "parsed": "uref-5ef62926d2a73b5982cab9cf546b59e75cd72b1c57c5eaaf2a01c90705fb6b9b-007" - }, - "name": { - "cl_type": "String", - "bytes": "2900000063657031385f636f6e74726163745f7061636b6167655f6163636573735f434c49434b312054657374", - "parsed": "cep18_contract_package_access_CLICK1 Test" - } + "AddKeys": [ + { + "name": "cep18_contract_package_access_CLICKT Test", + "key": "uref-d8a4e0ed98096e0df3263828e46c1f6758255853d3abcb32ebaf0776483a46bb-007" } - } + ] } }, { - "key": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", - "kind": "Identity" - }, - { - "key": "entity-system-989625d28e779749e3cacc7367403dd459c8e7082937436f16d5dbff7aec8292", - "kind": "Identity" - }, - { - "key": "package-b94115849598c8b83bb94dd7ea211630a9e23827ceb089a8674e1518a8257a1a", - "kind": "Identity" - }, - { - "key": "entry-point-v1-entity-system-989625d28e779749e3cacc7367403dd459c8e7082937436f16d5dbff7aec8292-21bddc7e4379ba445c7118cb51962954e0d1e5aa5cacc0c4ff6095b57eb9fb33", + "key": "hash-976b060d3ae59125f3521c5dd16ca5f55c9862b37a9effa05c3770a9a4d7b870", "kind": "Identity" }, { - "key": "uref-2b130d8c617d24bd5366bdd2843db98a5e93e594ed827ace443b4ea6e4772f01-000", - "kind": { - "Write": { - "CLValue": { - "cl_type": "Unit", - "bytes": "", - "parsed": null - } - } - } - }, - { - "key": "balance-2b130d8c617d24bd5366bdd2843db98a5e93e594ed827ace443b4ea6e4772f01", + "key": "message-topic-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", "kind": { "Write": { - "CLValue": { - "cl_type": "U512", - "bytes": "00", - "parsed": "0" + "MessageTopic": { + "message_count": 0, + "blocktime": 1734512599230, + "topic_name": "events" } } } }, { - "key": "byte-code-v1-wasm-800e35cfd87b0f36479afdaf68725cd7ef0f503e8d541ed33afad4a18c7fa6eb", + "key": "hash-feb66b9d1f45bf0a4c11c2048c2b5c839f191128f883ad24d1c2508fc6ad285d", "kind": { "Write": { - "ByteCode": { - "kind": "V1CasperWasm", + "ContractWasm": { "bytes": "01020304" } } } }, { - "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-be138e764d5f26cd174471e18c82a7bef961da4c7e7ade7df068038aebdda9bf", - "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "0263c283118dd23b4ceb6fb4eabe219941ca795a9588f5e09c1438cf7e4904d28507", - "parsed": "uref-63c283118dd23b4ceb6fb4eabe219941ca795a9588f5e09c1438cf7e4904d285-007" - }, - "name": { - "cl_type": "String", - "bytes": "08000000646563696d616c73", - "parsed": "decimals" - } - } - } - } - }, - { - "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-f26520fac960fb1abac44a358923ab6a064baffb1707c885886d157f66c55209", - "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "02ed2804ba22d80b1cc8abb02f72e1d87e93b3186d1225cf286b733e29c4df61e807", - "parsed": "uref-ed2804ba22d80b1cc8abb02f72e1d87e93b3186d1225cf286b733e29c4df61e8-007" - }, - "name": { - "cl_type": "String", - "bytes": "10000000656e61626c655f6d696e745f6275726e", - "parsed": "enable_mint_burn" - } - } - } - } - }, - { - "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-7114a751e72d65a0290c975396374e0120ac8f3ddbb6e4e21e3c6810135b40d0", - "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "0208f9c3e110c4d5cbc6e8ce71dd0cd535e4f41b34fe88c26cf3cd222004373db307", - "parsed": "uref-08f9c3e110c4d5cbc6e8ce71dd0cd535e4f41b34fe88c26cf3cd222004373db3-007" - }, - "name": { - "cl_type": "String", - "bytes": "0b0000006576656e74735f6d6f6465", - "parsed": "events_mode" - } - } - } - } - }, - { - "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-69cade231fc487185af830cfe041ce668a4763ab02ee5989b8baac6bee7e1a22", + "key": "hash-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4", "kind": { "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "025aa675065318455e193fc341bb4750174283add94ca195f71e4b4104e4c84b1a07", - "parsed": "uref-5aa675065318455e193fc341bb4750174283add94ca195f71e4b4104e4c84b1a-007" - }, - "name": { - "cl_type": "String", - "bytes": "040000006e616d65", - "parsed": "name" - } - } - } - } - }, - { - "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-eef0c71bbea5a76f1da01cb395e12bc0388bec279852100e17e3843b3e559999", - "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "02977f30046644e5c7a7e8cb297e5f8971a1680543dc31d1066ab4870c575b8a0d07", - "parsed": "uref-977f30046644e5c7a7e8cb297e5f8971a1680543dc31d1066ab4870c575b8a0d-007" - }, - "name": { - "cl_type": "String", - "bytes": "0600000073796d626f6c", - "parsed": "symbol" - } - } - } - } - }, - { - "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-f99c57f016ee238df5bcdf8bec27869b1ba087a415050a9c6668644eeda11af0", - "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "025b2eb4a8703d23be50b30625811388876b10ea57c6f79e613a144b59bc8eea1d07", - "parsed": "uref-5b2eb4a8703d23be50b30625811388876b10ea57c6f79e613a144b59bc8eea1d-007" - }, - "name": { - "cl_type": "String", - "bytes": "0c000000746f74616c5f737570706c79", - "parsed": "total_supply" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-1e3c8f46f39b1ee1cbadb774ffaf842226b7cbf1fef3bbc04abfa80b86daca11", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "allowance", - "args": [ - { - "name": "owner", - "cl_type": "Key" - }, - { - "name": "spender", - "cl_type": "Key" + "Contract": { + "contract_package_hash": "contract-package-976b060d3ae59125f3521c5dd16ca5f55c9862b37a9effa05c3770a9a4d7b870", + "contract_wasm_hash": "contract-wasm-feb66b9d1f45bf0a4c11c2048c2b5c839f191128f883ad24d1c2508fc6ad285d", + "named_keys": [ + { + "name": "decimals", + "key": "uref-127a8c723a6df941dae0d26a9c181c26f1a80f86eaeb12eed786b4c9715b5b55-007" + }, + { + "name": "enable_mint_burn", + "key": "uref-c09b022180e6a9221dfefe6ae62c6c8a93e09031742f284ffb64c5c1ddd23395-007" + }, + { + "name": "events_mode", + "key": "uref-a0cb023abb00bf87f9898b5fdce36569d5b87b7891e2b5023e663a5bc78134e0-007" + }, + { + "name": "name", + "key": "uref-6702ca24ad114bf8fe2e7e2169461b98e11f12ad7ad191d6e4b6594183f64ecf-007" + }, + { + "name": "symbol", + "key": "uref-644d8cce1ad2d0fb9a783a72e9542a4edc3e88e62f13e55d3d6a2899cd205619-007" + }, + { + "name": "total_supply", + "key": "uref-411a53953e73ddd23a115ad96e7ccb53e1867ec4beec03e62b8c7aa400be56fe-007" + } + ], + "entry_points": [ + { + "name": "allowance", + "entry_point": { + "name": "allowance", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "spender", + "cl_type": "Key" + } + ], + "ret": "U256", + "access": "Public", + "entry_point_type": "Called" } - ], - "ret": "U256", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-44528c1898e30df62037a76e0c45123f4f4437336ca63236b10ebfc16a5edb78", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "approve", - "args": [ - { - "name": "spender", - "cl_type": "Key" - }, - { - "name": "amount", - "cl_type": "U256" + }, + { + "name": "approve", + "entry_point": { + "name": "approve", + "args": [ + { + "name": "spender", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" } - ], - "ret": "Unit", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-fcc296caa05679d0d11121e7629b29f222a857018f50985046b73a56e9a10701", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "balance_of", - "args": [ - { - "name": "address", - "cl_type": "Key" + }, + { + "name": "balance_of", + "entry_point": { + "name": "balance_of", + "args": [ + { + "name": "address", + "cl_type": "Key" + } + ], + "ret": "U256", + "access": "Public", + "entry_point_type": "Called" } - ], - "ret": "U256", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-768c370eb010604bd19029a409dca8b5fbf9af9bc14a36c2b294a2a7a922161e", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "burn", - "args": [ - { - "name": "owner", - "cl_type": "Key" - }, - { - "name": "amount", - "cl_type": "U256" + }, + { + "name": "burn", + "entry_point": { + "name": "burn", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" } - ], - "ret": "Unit", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-bab8615f758ed79acb7dd7577b1a6c12d625d1a19592a2b1ded0dc352407e4d5", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "change_events_mode", - "args": [ - { - "name": "events_mode", - "cl_type": "U8" + }, + { + "name": "change_events_mode", + "entry_point": { + "name": "change_events_mode", + "args": [ + { + "name": "events_mode", + "cl_type": "U8" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" } - ], - "ret": "Unit", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-82a811993cf9ccb5e46c9608c69d86e3c9b7b499520fd48cdca1424f2a08efdc", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "change_security", - "args": [], - "ret": "Unit", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-45ffbf1854843af5eeec6b167e14a9e97bdb526e66205b07559d4fb3928fb11e", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "condor", - "args": [], - "ret": "String", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-be138e764d5f26cd174471e18c82a7bef961da4c7e7ade7df068038aebdda9bf", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "decimals", - "args": [], - "ret": "U8", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-ac07c23dc90a33282d553af890e30e62335c5ae986629d643778e2d4516f26ad", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "decrease_allowance", - "args": [ - { - "name": "spender", - "cl_type": "Key" - }, - { - "name": "amount", - "cl_type": "U256" + }, + { + "name": "change_security", + "entry_point": { + "name": "change_security", + "args": [], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" } - ], - "ret": "Unit", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-a7e05838c728d16c4ba3e1980b6729c857ef4c21d1b0c34e6eefbb486cdc2b89", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "increase_allowance", - "args": [ - { - "name": "spender", - "cl_type": "Key" - }, - { - "name": "amount", - "cl_type": "U256" + }, + { + "name": "condor", + "entry_point": { + "name": "condor", + "args": [], + "ret": "String", + "access": "Public", + "entry_point_type": "Called" } - ], - "ret": "Unit", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-4ca60287ae6129662475a8ce0d41c450d072b2430a8759f6178adeeff38523da", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "init", - "args": [], - "ret": "Unit", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-fc79236fd0e4521c8feddcc2094c6a0ea04fcaafb17fef63ef060744a6bab401", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "migrate_sec_keys", - "args": [ - { - "name": "events", - "cl_type": "Bool" - }, - { - "name": "revert", - "cl_type": "Bool" + }, + { + "name": "decimals", + "entry_point": { + "name": "decimals", + "args": [], + "ret": "U8", + "access": "Public", + "entry_point_type": "Called" } - ], - "ret": "Unit", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-18bff854e9d908cf20fb1db53a47ab69968917b53b8c71371e7dd0f88b363e60", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "migrate_user_allowance_keys", - "args": [ - { - "name": "events", - "cl_type": "Bool" - }, - { - "name": "revert", - "cl_type": "Bool" + }, + { + "name": "decrease_allowance", + "entry_point": { + "name": "decrease_allowance", + "args": [ + { + "name": "spender", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" } - ], - "ret": "Unit", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-1e7babf918642bd9636d3c121691bd85534b41084a5c22fe1e2bf196224dade6", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "migrate_user_balance_keys", - "args": [ - { - "name": "events", - "cl_type": "Bool" - }, - { - "name": "revert", - "cl_type": "Bool" + }, + { + "name": "increase_allowance", + "entry_point": { + "name": "increase_allowance", + "args": [ + { + "name": "spender", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" } - ], - "ret": "Unit", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-233964bb1dc667b37a8abbb938d7647b2c4ab41f0c26dbbcd26c62e7870f72ba", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "mint", - "args": [ - { - "name": "owner", - "cl_type": "Key" - }, - { - "name": "amount", - "cl_type": "U256" + }, + { + "name": "init", + "entry_point": { + "name": "init", + "args": [], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" } - ], - "ret": "Unit", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-69cade231fc487185af830cfe041ce668a4763ab02ee5989b8baac6bee7e1a22", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "name", - "args": [], - "ret": "String", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-eef0c71bbea5a76f1da01cb395e12bc0388bec279852100e17e3843b3e559999", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "symbol", - "args": [], - "ret": "String", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-f99c57f016ee238df5bcdf8bec27869b1ba087a415050a9c6668644eeda11af0", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "total_supply", - "args": [], - "ret": "U256", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-3820ce25e54df0470fb738e3e0f63ee50b2719cf2680da2bdb579e21aebc8f63", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "transfer", - "args": [ - { - "name": "recipient", - "cl_type": "Key" - }, - { - "name": "amount", - "cl_type": "U256" + }, + { + "name": "migrate_sec_keys", + "entry_point": { + "name": "migrate_sec_keys", + "args": [], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" } - ], - "ret": "Unit", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-44208043191e40d3417df6878e1a23894172cf37cf2e4444384ada99e25430e7", - "kind": { - "Write": { - "EntryPoint": { - "V1CasperVm": { - "name": "transfer_from", - "args": [ - { - "name": "owner", - "cl_type": "Key" - }, - { - "name": "recipient", - "cl_type": "Key" - }, - { - "name": "amount", - "cl_type": "U256" + }, + { + "name": "migrate_user_allowance_keys", + "entry_point": { + "name": "migrate_user_allowance_keys", + "args": [], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" } - ], - "ret": "Unit", - "access": "Public", - "entry_point_type": "Called", - "entry_point_payment": "Caller" - } - } - } - } - }, - { - "key": "entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58", - "kind": { - "Write": { - "AddressableEntity": { - "protocol_version": "2.0.0", - "entity_kind": { - "SmartContract": "VmCasperV1" - }, - "package_hash": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", - "byte_code_hash": "byte-code-800e35cfd87b0f36479afdaf68725cd7ef0f503e8d541ed33afad4a18c7fa6eb", - "main_purse": "uref-2b130d8c617d24bd5366bdd2843db98a5e93e594ed827ace443b4ea6e4772f01-007", - "associated_keys": [ + }, { - "account_hash": "account-hash-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f", - "weight": 1 - } - ], - "action_thresholds": { - "deployment": 1, - "upgrade_management": 1, - "key_management": 1 - }, - "message_topics": [ + "name": "migrate_user_balance_keys", + "entry_point": { + "name": "migrate_user_balance_keys", + "args": [], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "mint", + "entry_point": { + "name": "mint", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "name", + "entry_point": { + "name": "name", + "args": [], + "ret": "String", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "symbol", + "entry_point": { + "name": "symbol", + "args": [], + "ret": "String", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "total_supply", + "entry_point": { + "name": "total_supply", + "args": [], + "ret": "U256", + "access": "Public", + "entry_point_type": "Called" + } + }, { - "topic_name": "errors", - "topic_name_hash": "b38b3a8f7a7cb169b9869f1b660e328df63941f4f078d284a0058140375ec7fc" + "name": "transfer", + "entry_point": { + "name": "transfer", + "args": [ + { + "name": "recipient", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } }, { - "topic_name": "events", - "topic_name_hash": "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1" + "name": "transfer_from", + "entry_point": { + "name": "transfer_from", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "recipient", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } } - ] + ], + "protocol_version": "2.0.0" } } } }, { - "key": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", + "key": "hash-976b060d3ae59125f3521c5dd16ca5f55c9862b37a9effa05c3770a9a4d7b870", "kind": { "Write": { - "Package": { + "ContractPackage": { + "access_key": "uref-d8a4e0ed98096e0df3263828e46c1f6758255853d3abcb32ebaf0776483a46bb-007", "versions": [ { - "entity_version_key": { - "protocol_version_major": 2, - "entity_version": 1 - }, - "addressable_entity_hash": "addressable-entity-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58" + "protocol_version_major": 2, + "contract_version": 1, + "contract_hash": "contract-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4" } ], "disabled_versions": [], @@ -926,48 +609,18 @@ } }, { - "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-b38b3a8f7a7cb169b9869f1b660e328df63941f4f078d284a0058140375ec7fc", + "key": "account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4", "kind": { - "Write": { - "MessageTopic": { - "message_count": 0, - "blocktime": 1721206325167 + "AddKeys": [ + { + "name": "cep18_contract_hash_CLICKT Test", + "key": "entity-contract-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4" } - } + ] } }, { - "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", - "kind": { - "Write": { - "MessageTopic": { - "message_count": 0, - "blocktime": 1721206325167 - } - } - } - }, - { - "key": "named-key-entity-account-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f-1b97fc9e53ff59b389432fd44a90c5d773bb65a2933b8196a5c441724819f8d6", - "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "1102d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58", - "parsed": "entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58" - }, - "name": { - "cl_type": "String", - "bytes": "1f00000063657031385f636f6e74726163745f686173685f434c49434b312054657374", - "parsed": "cep18_contract_hash_CLICK1 Test" - } - } - } - } - }, - { - "key": "uref-a6b391748c16be8f8c000b5658884cea53332255596e0537667e71f2d358af90-000", + "key": "uref-e2e2f2ee35996daeb226a3d756bc1bd245e635c2f66ea6c0acd15859bc543519-000", "kind": { "Write": { "CLValue": { @@ -979,26 +632,18 @@ } }, { - "key": "named-key-entity-account-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f-50e82d25e72fda1f67a69b3ac3a3a56565971c80577ba72df18d6fbd73f2bb1d", + "key": "account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4", "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "02a6b391748c16be8f8c000b5658884cea53332255596e0537667e71f2d358af9007", - "parsed": "uref-a6b391748c16be8f8c000b5658884cea53332255596e0537667e71f2d358af90-007" - }, - "name": { - "cl_type": "String", - "bytes": "2200000063657031385f636f6e74726163745f76657273696f6e5f434c49434b312054657374", - "parsed": "cep18_contract_version_CLICK1 Test" - } + "AddKeys": [ + { + "name": "cep18_contract_version_CLICKT Test", + "key": "uref-e2e2f2ee35996daeb226a3d756bc1bd245e635c2f66ea6c0acd15859bc543519-007" } - } + ] } }, { - "key": "uref-419179eebe4ea6427e77a58aae6fac945c4d4135d042ff800d0be71ac17d7a8d-000", + "key": "uref-11aa02dd78243651d098b056c7a3761c9ba2024da11df432b350c36d6bc15d33-000", "kind": { "Write": { "CLValue": { @@ -1010,80 +655,52 @@ } }, { - "key": "named-key-entity-account-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f-45ffbf1854843af5eeec6b167e14a9e97bdb526e66205b07559d4fb3928fb11e", + "key": "account-hash-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4", "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "02419179eebe4ea6427e77a58aae6fac945c4d4135d042ff800d0be71ac17d7a8d07", - "parsed": "uref-419179eebe4ea6427e77a58aae6fac945c4d4135d042ff800d0be71ac17d7a8d-007" - }, - "name": { - "cl_type": "String", - "bytes": "06000000636f6e646f72", - "parsed": "condor" - } + "AddKeys": [ + { + "name": "condor", + "key": "uref-11aa02dd78243651d098b056c7a3761c9ba2024da11df432b350c36d6bc15d33-007" } - } + ] } }, { - "key": "entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58", + "key": "hash-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4", "kind": "Identity" }, { - "key": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", + "key": "hash-976b060d3ae59125f3521c5dd16ca5f55c9862b37a9effa05c3770a9a4d7b870", "kind": "Identity" }, { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-4ca60287ae6129662475a8ce0d41c450d072b2430a8759f6178adeeff38523da", + "key": "hash-feb66b9d1f45bf0a4c11c2048c2b5c839f191128f883ad24d1c2508fc6ad285d", "kind": "Identity" }, { - "key": "byte-code-v1-wasm-800e35cfd87b0f36479afdaf68725cd7ef0f503e8d541ed33afad4a18c7fa6eb", - "kind": "Identity" - }, - { - "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-c45567f4859db5b100d4c45a9638973ccdf0a7068a2a50f7a1c8709d5e84fbd2", + "key": "hash-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4", "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "10ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", - "parsed": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4" - }, - "name": { - "cl_type": "String", - "bytes": "0c0000007061636b6167655f68617368", - "parsed": "package_hash" - } + "AddKeys": [ + { + "name": "package_hash", + "key": "package-976b060d3ae59125f3521c5dd16ca5f55c9862b37a9effa05c3770a9a4d7b870" } - } + ] } }, { - "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-d447c1e29c0d374421e2c3f290c580a73aa748eba54e1b6bb3da0acdd4cdcee5", + "key": "hash-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4", "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "1102d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58", - "parsed": "entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58" - }, - "name": { - "cl_type": "String", - "bytes": "0d000000636f6e74726163745f68617368", - "parsed": "contract_hash" - } + "AddKeys": [ + { + "name": "contract_hash", + "key": "entity-contract-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4" } - } + ] } }, { - "key": "uref-e164c15c5fb035160a0d105a467fa4b6e7562746ffd2bdad6e5e3220878df19a-000", + "key": "uref-5014b553c6c195c6327fdbdf4c6f411cd1ad99aecaa1add06d6459b9452d66f5-000", "kind": { "Write": { "CLValue": { @@ -1095,26 +712,18 @@ } }, { - "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-bce0df2b23ec57062235795dd9b7a1a6dc07885918c6cee5c5859a9df3d498e0", + "key": "hash-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4", "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "02e164c15c5fb035160a0d105a467fa4b6e7562746ffd2bdad6e5e3220878df19a07", - "parsed": "uref-e164c15c5fb035160a0d105a467fa4b6e7562746ffd2bdad6e5e3220878df19a-007" - }, - "name": { - "cl_type": "String", - "bytes": "0a000000616c6c6f77616e636573", - "parsed": "allowances" - } + "AddKeys": [ + { + "name": "allowances", + "key": "uref-5014b553c6c195c6327fdbdf4c6f411cd1ad99aecaa1add06d6459b9452d66f5-007" } - } + ] } }, { - "key": "uref-f032be9ae9bd295ec0403aa886d7247d27c296751176bd88d3b02c996626fd05-000", + "key": "uref-85a731470d19fb7c4986f85f8bc889aec3bd578c5e3f499ce8ff3311fde230dc-000", "kind": { "Write": { "CLValue": { @@ -1126,38 +735,30 @@ } }, { - "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-058ed80a0c85bcf8470ac5b8ca4c14f1086c3cd5ae00f1f0592fee3addf4073c", + "key": "hash-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4", "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "02f032be9ae9bd295ec0403aa886d7247d27c296751176bd88d3b02c996626fd0507", - "parsed": "uref-f032be9ae9bd295ec0403aa886d7247d27c296751176bd88d3b02c996626fd05-007" - }, - "name": { - "cl_type": "String", - "bytes": "0800000062616c616e636573", - "parsed": "balances" - } + "AddKeys": [ + { + "name": "balances", + "key": "uref-85a731470d19fb7c4986f85f8bc889aec3bd578c5e3f499ce8ff3311fde230dc-007" } - } + ] } }, { - "key": "dictionary-ede600b099900d357796a3e23eecb1e5277bdfb93297c36f0c039d2c3a5c6557", + "key": "dictionary-02af2d6b9eb8e3c6c5e7403f44e01766ef86516bc40e674b360ea6d1c8ae21af", "kind": { "Write": { "CLValue": { "cl_type": "Any", - "bytes": "08000000070080c6a47e8d030720000000f032be9ae9bd295ec0403aa886d7247d27c296751176bd88d3b02c996626fd053000000045514869587777376d477171476d7946376a5672365a7a5449506f66664f72356b6f6f2f765146647352386b44773d3d", + "bytes": "08000000070080c6a47e8d03072000000085a731470d19fb7c4986f85f8bc889aec3bd578c5e3f499ce8ff3311fde230dc2c000000414a2b682f4167493036573536703836394d70386a445a56566f2f664e34324b2f66696e3557355971372f55", "parsed": null } } } }, { - "key": "uref-6e6c902ff1c50971ab62f788d0fe0171ebffceb91be9861c02ce55993ae6bcdc-000", + "key": "uref-f9afa20f6ac96ed3aac01ddeecfef4067aee7ad0b90ba12ce76a02cf29557817-000", "kind": { "Write": { "CLValue": { @@ -1169,199 +770,52 @@ } }, { - "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5f4fd818ad44d4ae056e151759a8585de97a1c7c4d53ceecac6631f9fbb39ab6", + "key": "hash-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4", "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "026e6c902ff1c50971ab62f788d0fe0171ebffceb91be9861c02ce55993ae6bcdc07", - "parsed": "uref-6e6c902ff1c50971ab62f788d0fe0171ebffceb91be9861c02ce55993ae6bcdc-007" - }, - "name": { - "cl_type": "String", - "bytes": "0f00000073656375726974795f626164676573", - "parsed": "security_badges" - } + "AddKeys": [ + { + "name": "security_badges", + "key": "uref-f9afa20f6ac96ed3aac01ddeecfef4067aee7ad0b90ba12ce76a02cf29557817-007" } - } + ] } }, { - "key": "dictionary-30b94b42bb8ec2d32652ac73cf74031df44c1327c946924c5c9ffe82a94b3dd0", + "key": "dictionary-a083f799ef84f75134c43548f59d20a3fb41c9993ba5295b000c1541c929ae98", "kind": { "Write": { "CLValue": { "cl_type": "Any", - "bytes": "010000000003200000006e6c902ff1c50971ab62f788d0fe0171ebffceb91be9861c02ce55993ae6bcdc3000000045514869587777376d477171476d7946376a5672365a7a5449506f66664f72356b6f6f2f765146647352386b44773d3d", - "parsed": null - } - } - } - }, - { - "key": "uref-08f9c3e110c4d5cbc6e8ce71dd0cd535e4f41b34fe88c26cf3cd222004373db3-000", - "kind": "Identity" - }, - { - "key": "uref-9d1bc561e4c28250e3595b12b78989409f69ad336a8e398b4e9f6b91b3c28317-000", - "kind": { - "Write": { - "CLValue": { - "cl_type": "Unit", - "bytes": "", - "parsed": null - } - } - } - }, - { - "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-0c12f81c2baaa55048b1b7f396d105681463f5ce8f43e9de903369fb55f29947", - "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "029d1bc561e4c28250e3595b12b78989409f69ad336a8e398b4e9f6b91b3c2831707", - "parsed": "uref-9d1bc561e4c28250e3595b12b78989409f69ad336a8e398b4e9f6b91b3c28317-007" - }, - "name": { - "cl_type": "String", - "bytes": "080000005f5f6576656e7473", - "parsed": "__events" - } - } - } - } - }, - { - "key": "uref-ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae4-000", - "kind": { - "Write": { - "CLValue": { - "cl_type": "U32", - "bytes": "00000000", - "parsed": 0 - } - } - } - }, - { - "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-dd93feca0896fb86350700d69861072bad6eae9c93e5f2ffeb43f2daa4f6fcc1", - "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "02ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae407", - "parsed": "uref-ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae4-007" - }, - "name": { - "cl_type": "String", - "bytes": "0f0000005f5f6576656e74735f6c656e677468", - "parsed": "__events_length" - } - } - } - } - }, - { - "key": "uref-8f51f4b1f048ba99b5b14e1021cfdb743af05c5c4452dd0350c5649a48cd3d09-000", - "kind": { - "Write": { - "CLValue": { - "cl_type": { - "Map": { - "key": "String", - "value": { - "List": { - "Tuple2": [ - "String", - "Any" - ] - } - } - } - }, - "bytes": "09000000040000004275726e02000000050000006f776e65720b06000000616d6f756e7407100000004368616e67654576656e74734d6f6465010000000b0000006576656e74735f6d6f6465030e0000004368616e67655365637572697479020000000500000061646d696e0b0e0000007365635f6368616e67655f6d6170110b03110000004465637265617365416c6c6f77616e636504000000050000006f776e65720b070000007370656e6465720b09000000616c6c6f77616e63650707000000646563725f62790711000000496e637265617365416c6c6f77616e636504000000050000006f776e65720b070000007370656e6465720b09000000616c6c6f77616e63650706000000696e635f627907040000004d696e740200000009000000726563697069656e740b06000000616d6f756e74070c000000536574416c6c6f77616e636503000000050000006f776e65720b070000007370656e6465720b09000000616c6c6f77616e636507080000005472616e73666572030000000600000073656e6465720b09000000726563697069656e740b06000000616d6f756e74070c0000005472616e7366657246726f6d04000000070000007370656e6465720b050000006f776e65720b09000000726563697069656e740b06000000616d6f756e7407", + "bytes": "01000000000320000000f9afa20f6ac96ed3aac01ddeecfef4067aee7ad0b90ba12ce76a02cf295578172c000000414a2b682f4167493036573536703836394d70386a445a56566f2f664e34324b2f66696e3557355971372f55", "parsed": null } } } }, { - "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-1668218bcb4abcca6eb423a234252322df6319372653092475d173c00714a089", - "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "028f51f4b1f048ba99b5b14e1021cfdb743af05c5c4452dd0350c5649a48cd3d0907", - "parsed": "uref-8f51f4b1f048ba99b5b14e1021cfdb743af05c5c4452dd0350c5649a48cd3d09-007" - }, - "name": { - "cl_type": "String", - "bytes": "0f0000005f5f6576656e74735f736368656d61", - "parsed": "__events_schema" - } - } - } - } - }, - { - "key": "uref-8474015e6fe46390ce16ebe4f7f6dd9d88b9a1def98ae4c32f8808b53f66d582-000", - "kind": { - "Write": { - "CLValue": { - "cl_type": "String", - "bytes": "03000000312e31", - "parsed": "1.1" - } - } - } - }, - { - "key": "named-key-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-6cb1f5fa1d62a0d84f41d492ee1526320c547fd54ef072398e9aec731475e9d0", - "kind": { - "Write": { - "NamedKey": { - "named_key": { - "cl_type": "Key", - "bytes": "028474015e6fe46390ce16ebe4f7f6dd9d88b9a1def98ae4c32f8808b53f66d58207", - "parsed": "uref-8474015e6fe46390ce16ebe4f7f6dd9d88b9a1def98ae4c32f8808b53f66d582-007" - }, - "name": { - "cl_type": "String", - "bytes": "140000005f5f6576656e74735f6365735f76657273696f6e", - "parsed": "__events_ces_version" - } - } - } - } - }, - { - "key": "uref-08f9c3e110c4d5cbc6e8ce71dd0cd535e4f41b34fe88c26cf3cd222004373db3-000", + "key": "uref-a0cb023abb00bf87f9898b5fdce36569d5b87b7891e2b5023e663a5bc78134e0-000", "kind": "Identity" }, { - "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "key": "message-topic-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", "kind": "Identity" }, { - "key": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-0", + "key": "message-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-0", "kind": { "Write": { - "Message": "message-checksum-de44efda117cc02be18edf564107308f982498f7a781e7496f7f20d05957bb3c" + "Message": "message-checksum-4f9c3b0e96c2fd77a8a0d11c123c23ee4251c183707307205ae02ecd29c3385e" } } }, { - "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "key": "message-topic-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", "kind": { "Write": { "MessageTopic": { "message_count": 1, - "blocktime": 1721206325167 + "blocktime": 1734512599230, + "topic_name": "events" } } } @@ -1377,9 +831,9 @@ "U64" ] }, - "bytes": "af3fe4bf900100000100000000000000", + "bytes": "bee001d9930100000100000000000000", "parsed": [ - 1721206325167, + 1734512599230, 1 ] } @@ -1387,41 +841,13 @@ } }, { - "key": "uref-ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae4-000", - "kind": "Identity" - }, - { - "key": "dictionary-17125c888001cf5f36a03240f55838b5d9e09172292589fff4a7b25ae6e6c5b2", + "key": "balance-hold-01e542fb9f5dc6d06d849e2ac3ad6cc5cd4b7c71392dc8759c0898f4ab04439520bee001d993010000", "kind": { - "Write": { - "CLValue": { - "cl_type": "Any", - "bytes": "3c000000380000000a0000006576656e745f4d696e741101e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f070080c6a47e8d030e03200000009d1bc561e4c28250e3595b12b78989409f69ad336a8e398b4e9f6b91b3c283170100000030", - "parsed": null - } - } + "Prune": "balance-hold-01e542fb9f5dc6d06d849e2ac3ad6cc5cd4b7c71392dc8759c0898f4ab04439520bee001d993010000" } }, { - "key": "uref-ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae4-000", - "kind": { - "Write": { - "CLValue": { - "cl_type": "U32", - "bytes": "01000000", - "parsed": 1 - } - } - } - }, - { - "key": "balance-hold-016f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee042215af3fe4bf90010000", - "kind": { - "Prune": "balance-hold-016f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee042215af3fe4bf90010000" - } - }, - { - "key": "balance-hold-006f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee042215af3fe4bf90010000", + "key": "balance-hold-00e542fb9f5dc6d06d849e2ac3ad6cc5cd4b7c71392dc8759c0898f4ab04439520bee001d993010000", "kind": { "Write": { "CLValue": { @@ -1433,29 +859,17 @@ } }, { - "key": "entity-system-8499396a854bbe7e172a5ab675caf3c7b944f57e43cee5b7563971678f8650b2", - "kind": "Identity" - }, - { - "key": "entity-system-989625d28e779749e3cacc7367403dd459c8e7082937436f16d5dbff7aec8292", - "kind": "Identity" - }, - { - "key": "entity-system-da2faf76ab117ec370d10f994e9407ff014ab974bedc50e58425a9e35428dd0c", - "kind": "Identity" - }, - { - "key": "bid-addr-011c50d14ca563d5afe0399a3da010bac01383f502bc7cce774cc03d858a94cf06", + "key": "bid-addr-01dcd0c38c46c9d5c7083aa1a46b430e8e460f97f8f0bf8444776ac925187acfcc", "kind": "Identity" }, { - "key": "bid-addr-041c50d14ca563d5afe0399a3da010bac01383f502bc7cce774cc03d858a94cf06a700000000000000", + "key": "bid-addr-04dcd0c38c46c9d5c7083aa1a46b430e8e460f97f8f0bf8444776ac925187acfcc0800000000000000", "kind": { "Write": { "BidKind": { "Credit": { - "validator_public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", - "era_id": 167, + "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "era_id": 8, "amount": "1000000000000" } } diff --git a/Casper.Network.SDK.Test/TestData/get-transaction-stored-v200.json b/Casper.Network.SDK.Test/TestData/get-transaction-stored-v200.json index 7fed4b1..db6c83d 100644 --- a/Casper.Network.SDK.Test/TestData/get-transaction-stored-v200.json +++ b/Casper.Network.SDK.Test/TestData/get-transaction-stored-v200.json @@ -2,271 +2,125 @@ "api_version": "2.0.0", "transaction": { "Version1": { - "hash": "3fe455e770f5464234b1321bcb6bd02c553649f9a8a468e5a37570f24631968d", - "header": { - "chain_name": "casper-net-1", - "timestamp": "2024-07-17T09:03:11.866Z", + "hash": "173251b375edd42e3ee43a05ff7d1996380c59192a785884a8cfdd574d86aa9a", + "payload": { + "initiator_addr": { + "PublicKey": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a" + }, + "timestamp": "2024-12-18T10:27:48.633Z", "ttl": "30m", - "body_hash": "07ed52f990a206153d2a29f6a42eb754009c4b120981dd3fd0842d4057ee7484", + "chain_name": "casper-net-1", "pricing_mode": { - "Fixed": { - "gas_price_tolerance": 1 + "PaymentLimited": { + "payment_amount": 3000000000, + "gas_price_tolerance": 1, + "standard_payment": true } }, - "initiator_addr": { - "AccountHash": "account-hash-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f" - } - }, - "body": { - "args": [ - [ - "recipient", - { - "cl_type": "Key", - "bytes": "00e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f", - "parsed": "account-hash-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f" - } - ], - [ - "amount", - { - "cl_type": "U256", - "bytes": "02d930", - "parsed": "12505" + "fields": { + "args": { + "Named": [ + [ + "recipient", + { + "cl_type": "Key", + "bytes": "00e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f", + "parsed": "account-hash-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f" + } + ], + [ + "amount", + { + "cl_type": "U256", + "bytes": "02a861", + "parsed": "25000" + } + ] + ] + }, + "entry_point": { + "Custom": "transfer" + }, + "scheduling": "Standard", + "target": { + "Stored": { + "id": { + "ByPackageName": { + "name": "cep18_contract_package_CLICKT Test", + "version": null + } + }, + "runtime": "VmCasperV1" } - ] - ], - "target": { - "Stored": { - "id": { - "ByPackageName": { - "name": "cep18_contract_package_CLICK1 Test", - "version": null - } - }, - "runtime": "VmCasperV1" } - }, - "entry_point": { - "Custom": "transfer" - }, - "transaction_category": 4, - "scheduling": "Standard" + } }, "approvals": [ { - "signer": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", - "signature": "010641446be631a751415fd88827bd0c804a390824c48b7f1272f37b827853dcf3ae5710e662570d28dab8a283994f4f096cbd1d4ebb2f7fd24fb05b2b9b61640f" + "signer": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", + "signature": "01b39caf891bbac7902078222651a6158bc7aa387355b8d37c70289f58330bc86ab920afcf51be5a523d2be30427150c228ba08aafb45ba927f762a270bdd8f006" } ] } }, "execution_info": { - "block_hash": "b6446a0508cbf2bffdf855213ea6eb43267c4dfacd667b50cf8d8f0098112a3b", - "block_height": 1984, + "block_hash": "5fd0ee0a8929245148a87cfe57355bf3762dbeabdef91155e1e13f23954d3f92", + "block_height": 1400, "execution_result": { "Version2": { "initiator": { - "AccountHash": "account-hash-e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f" + "PublicKey": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a" }, - "error_message": null, - "limit": "50000000000", - "consumed": "763310980", - "cost": "50000000000", - "payment": [], + "error_message": "ApiError::Formatting [18]", + "limit": "3000000000", + "consumed": "1241925", + "cost": "3000000000", "transfers": [], - "size_estimate": 363, + "size_estimate": 591, "effects": [ { - "key": "balance-hold-016f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee0422150b78eebf90010000", + "key": "balance-hold-01e542fb9f5dc6d06d849e2ac3ad6cc5cd4b7c71392dc8759c0898f4ab04439520b1414fd993010000", "kind": { "Write": { "CLValue": { "cl_type": "U512", - "bytes": "0500743ba40b", - "parsed": "50000000000" + "bytes": "04005ed0b2", + "parsed": "3000000000" } } } }, { - "key": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", - "kind": "Identity" - }, - { - "key": "entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58", - "kind": "Identity" - }, - { - "key": "package-ab86f3895a5b0d8cd15113ec159f425037aceb9bb1b464affd9db54b592accf4", - "kind": "Identity" - }, - { - "key": "entry-point-v1-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-3820ce25e54df0470fb738e3e0f63ee50b2719cf2680da2bdb579e21aebc8f63", - "kind": "Identity" - }, - { - "key": "byte-code-v1-wasm-800e35cfd87b0f36479afdaf68725cd7ef0f503e8d541ed33afad4a18c7fa6eb", - "kind": "Identity" - }, - { - "key": "dictionary-ede600b099900d357796a3e23eecb1e5277bdfb93297c36f0c039d2c3a5c6557", - "kind": "Identity" - }, - { - "key": "dictionary-b25d5021fc15e97434b4e8128804f5c8e7ce21e65bbe80d0ec344c23f93c7582", - "kind": "Identity" - }, - { - "key": "dictionary-ede600b099900d357796a3e23eecb1e5277bdfb93297c36f0c039d2c3a5c6557", + "key": "balance-hold-01e542fb9f5dc6d06d849e2ac3ad6cc5cd4b7c71392dc8759c0898f4ab04439520b1414fd993010000", "kind": { - "Write": { - "CLValue": { - "cl_type": "Any", - "bytes": "0800000007c38bc5a47e8d030720000000f032be9ae9bd295ec0403aa886d7247d27c296751176bd88d3b02c996626fd053000000045514869587777376d477171476d7946376a5672365a7a5449506f66664f72356b6f6f2f765146647352386b44773d3d", - "parsed": null - } - } + "Prune": "balance-hold-01e542fb9f5dc6d06d849e2ac3ad6cc5cd4b7c71392dc8759c0898f4ab04439520b1414fd993010000" } }, { - "key": "dictionary-b25d5021fc15e97434b4e8128804f5c8e7ce21e65bbe80d0ec344c23f93c7582", - "kind": { - "Write": { - "CLValue": { - "cl_type": "Any", - "bytes": "03000000023df40720000000f032be9ae9bd295ec0403aa886d7247d27c296751176bd88d3b02c996626fd052c000000414f4a664444755961716f61624958754e5776706e4e4d672b68393836766d53696a2b394156327848795150", - "parsed": null - } - } - } - }, - { - "key": "uref-08f9c3e110c4d5cbc6e8ce71dd0cd535e4f41b34fe88c26cf3cd222004373db3-000", - "kind": "Identity" - }, - { - "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", - "kind": "Identity" - }, - { - "key": "block-message-count-00000000000000000000000000000000000000000000000000000000000000", - "kind": "Identity" - }, - { - "key": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-1", - "kind": { - "Write": { - "Message": "message-checksum-f6a3531f88cf8e763916a16e95a64e2ef93b0567064830ad5912523fad71fbe2" - } - } - }, - { - "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", - "kind": { - "Write": { - "MessageTopic": { - "message_count": 2, - "blocktime": 1721206994955 - } - } - } - }, - { - "key": "block-message-count-00000000000000000000000000000000000000000000000000000000000000", - "kind": { - "Write": { - "CLValue": { - "cl_type": { - "Tuple2": [ - "U64", - "U64" - ] - }, - "bytes": "0b78eebf900100000200000000000000", - "parsed": [ - 1721206994955, - 2 - ] - } - } - } - }, - { - "key": "uref-ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae4-000", - "kind": "Identity" - }, - { - "key": "dictionary-55775426d9d4425e15abc8f1e7241ef945379bf5037bb346e3ca15a4fe1ccb50", - "kind": { - "Write": { - "CLValue": { - "cl_type": "Any", - "bytes": "5c000000580000000e0000006576656e745f5472616e736665721101e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f00e25f0c3b986aaa1a6c85ee356be99cd320fa1f7ceaf9928a3fbd015db11f240f02d9300e03200000009d1bc561e4c28250e3595b12b78989409f69ad336a8e398b4e9f6b91b3c283170100000035", - "parsed": null - } - } - } - }, - { - "key": "uref-ab3e2fffdfad1bea4c6cea416dc7b820f769dc1ce9071d524d63e704ff0c3ae4-000", - "kind": { - "Write": { - "CLValue": { - "cl_type": "U32", - "bytes": "06000000", - "parsed": 6 - } - } - } - }, - { - "key": "balance-hold-016f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee0422150b78eebf90010000", - "kind": { - "Prune": "balance-hold-016f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee0422150b78eebf90010000" - } - }, - { - "key": "balance-hold-006f8eee1d8ea5eee794ffe69a9008c9c8620eea284ee2c49bf686ae8eee0422150b78eebf90010000", + "key": "balance-hold-00e542fb9f5dc6d06d849e2ac3ad6cc5cd4b7c71392dc8759c0898f4ab04439520b1414fd993010000", "kind": { "Write": { "CLValue": { "cl_type": "U512", - "bytes": "0500743ba40b", - "parsed": "50000000000" + "bytes": "04005ed0b2", + "parsed": "3000000000" } } } }, { - "key": "entity-system-8499396a854bbe7e172a5ab675caf3c7b944f57e43cee5b7563971678f8650b2", - "kind": "Identity" - }, - { - "key": "entity-system-989625d28e779749e3cacc7367403dd459c8e7082937436f16d5dbff7aec8292", - "kind": "Identity" - }, - { - "key": "entity-system-da2faf76ab117ec370d10f994e9407ff014ab974bedc50e58425a9e35428dd0c", - "kind": "Identity" - }, - { - "key": "bid-addr-0165a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b1566213", - "kind": "Identity" - }, - { - "key": "bid-addr-0465a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b1566213b700000000000000", + "key": "bid-addr-01520037cd249ccbcfeb0b9feae07d8d4f7d922cf88adc4f3e8691f9d34ccc8d09", "kind": "Identity" }, { - "key": "bid-addr-0465a3d53119035ffe8560a67e355a80b2edaf2673fbd2d1d90b70a033b1566213b700000000000000", + "key": "bid-addr-04520037cd249ccbcfeb0b9feae07d8d4f7d922cf88adc4f3e8691f9d34ccc8d098100000000000000", "kind": { "Write": { "BidKind": { "Credit": { - "validator_public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", - "era_id": 183, - "amount": "50000000000" + "validator_public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "era_id": 129, + "amount": "3000000000" } } } @@ -276,4 +130,4 @@ } } } -} \ No newline at end of file +} diff --git a/Casper.Network.SDK/Types/StoredValue.cs b/Casper.Network.SDK/Types/StoredValue.cs index e1afa70..1fedfa4 100644 --- a/Casper.Network.SDK/Types/StoredValue.cs +++ b/Casper.Network.SDK/Types/StoredValue.cs @@ -44,7 +44,7 @@ public class StoredValue /// /// Stores a package. /// - public Package SmartContract { get; init; } + public Package Package { get; init; } /// /// A record of byte code. @@ -107,17 +107,6 @@ JsonSerializerOptions options var propertyValue = JsonSerializer.Deserialize(ref reader, propertyInfo.PropertyType, options); propertyInfo.SetValue(storedValue, propertyValue); } - else if(propertyName.Equals("LegacyTransfer", StringComparison.InvariantCultureIgnoreCase)) - { - reader.Read(); - - var serializerOptions = new JsonSerializerOptions(options); - serializerOptions.Converters.Add(new Transfer.TransferConverter()); - - var t = JsonSerializer.Deserialize(ref reader, serializerOptions); - propertyInfo = typeof(StoredValue).GetProperty("Transfer", BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); - propertyInfo?.SetValue(storedValue, t); - } else throw new JsonException($"Unknown property: {propertyName}."); } diff --git a/Casper.Network.SDK/Types/Transaction.cs b/Casper.Network.SDK/Types/Transaction.cs index 20f105a..42d2f69 100644 --- a/Casper.Network.SDK/Types/Transaction.cs +++ b/Casper.Network.SDK/Types/Transaction.cs @@ -76,14 +76,10 @@ public TransactionVersion Version public ITransactionScheduling Scheduling { get; set; } - public TransactionCategory Category { get; set; } - public ITransactionInvocation Invocation { get; init; } public interface ITransactionInvocation { - TransactionCategory Category { get; init; } - List RuntimeArgs { get; init; } CLValue GetRuntimeArgValue(string name); @@ -91,8 +87,6 @@ public interface ITransactionInvocation public abstract class TransactionInvocation : ITransactionInvocation { - public TransactionCategory Category { get; init; } - public List RuntimeArgs { get; init; } public CLValue GetRuntimeArgValue(string name) @@ -265,7 +259,6 @@ public static explicit operator Transaction(Deploy deploy) PricingMode = pricingMode, Approvals = deploy.Approvals, Scheduling = TransactionScheduling.Standard, - Category = deploy.Session is TransferDeployItem ? TransactionCategory.Mint : TransactionCategory.Large, Invocation = invocation, }; } diff --git a/Docs/Examples/NativeTransfer/Program.cs b/Docs/Examples/NativeTransfer/Program.cs index 0cfd95f..e81f4f0 100644 --- a/Docs/Examples/NativeTransfer/Program.cs +++ b/Docs/Examples/NativeTransfer/Program.cs @@ -40,7 +40,7 @@ public static async Task Main(string[] args) .Amount(25_000_000_000) .Id(DateUtils.ToEpochTime(DateTime.Now)) .ChainName(chainName) - .GasPriceTolerance(1) + .Payment(PricingMode.PaymentLimited(100_000_000, 1)) .Build(); // sign the transaction and send it to the network From b2a88a5052fe972c4d13cc331b1b2f38c4b9ccbf Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 18 Dec 2024 11:34:19 +0100 Subject: [PATCH 100/126] Fix test Signed-off-by: David Hernando --- Casper.Network.SDK.Test/TestData/legacy_transfer_v200.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Casper.Network.SDK.Test/TestData/legacy_transfer_v200.json b/Casper.Network.SDK.Test/TestData/legacy_transfer_v200.json index 1e4fb86..ac31e79 100644 --- a/Casper.Network.SDK.Test/TestData/legacy_transfer_v200.json +++ b/Casper.Network.SDK.Test/TestData/legacy_transfer_v200.json @@ -2,7 +2,7 @@ "api_version": "2.0.0", "block_header": null, "stored_value": { - "LegacyTransfer": { + "Transfer": { "deploy_hash": "cd91f138e82ddce5dfbb99c6bbf3f47caca439b81d7f43702cbebfa99bacbfd0", "from": "account-hash-87516c22bca9a14179ebbbe646c8f911153fe53626126c1ba24293517c2e04a2", "to": "account-hash-1265caa7cd80f31f882ab6f5623d89741c77cf3a36a309b54ed55fdc0be227c9", From 2c3699df7900555163236d7da5f65122a381bdaf Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 18 Dec 2024 11:35:37 +0100 Subject: [PATCH 101/126] fix test Signed-off-by: David Hernando --- Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs index 6b04ddc..e8f810f 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetEntityResultTest.cs @@ -45,7 +45,7 @@ public void GetEntityContractTest_v200() Assert.IsNotNull(result.Entity.Package); Assert.IsNotNull(result.Entity.MainPurse); Assert.AreEqual("byte-code-85def61e3ee02e10a1e845cfb8e8b2d9640a18f605333158027a24ed8569d895", result.Entity.ByteCodeHash); - Assert.AreEqual(TransactionRuntime.VmCasperV1().ToString(), result.Entity.EntityKind.SmartContract); + Assert.AreEqual(TransactionRuntime.VmCasperV1().ToString(), result.Entity.EntityKind.SmartContract.ToString()); Assert.AreEqual(1, result.Entity.ActionThresholds.KeyManagement); Assert.AreEqual(1, result.Entity.ActionThresholds.Deployment); Assert.AreEqual(1, result.Entity.ActionThresholds.UpgradeManagement); From eae9a169b284cb6edbc40e99f3df540b9b66bf52 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 18 Dec 2024 11:53:18 +0100 Subject: [PATCH 102/126] CSDK-196 Signed-off-by: David Hernando --- Casper.Network.SDK/Types/EntryPoint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Casper.Network.SDK/Types/EntryPoint.cs b/Casper.Network.SDK/Types/EntryPoint.cs index 1fba02e..636e28c 100644 --- a/Casper.Network.SDK/Types/EntryPoint.cs +++ b/Casper.Network.SDK/Types/EntryPoint.cs @@ -117,7 +117,7 @@ public enum EntryPointPayment /// /// Will cover cost to execute self but not cost of any subsequent invoked contracts. /// - SelfOnly, + DirectInvocationOnly, /// /// Will cover cost to execute self and the cost of any subsequent invoked contracts. /// From 24ef097cdfcae4140a8ab232bb4966e5002a2a55 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 18 Dec 2024 12:34:31 +0100 Subject: [PATCH 103/126] fix unit test Signed-off-by: David Hernando --- Casper.Network.SDK.Test/SSETypesTest.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/Casper.Network.SDK.Test/SSETypesTest.cs b/Casper.Network.SDK.Test/SSETypesTest.cs index 79b410f..a71f38d 100644 --- a/Casper.Network.SDK.Test/SSETypesTest.cs +++ b/Casper.Network.SDK.Test/SSETypesTest.cs @@ -83,19 +83,11 @@ public void TransactionProcessedTest() value.TransactionHash.Version1); Assert.AreEqual("01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", value.InitiatorAddr.PublicKey.ToString().ToLower()); - Assert.AreEqual("b3fc60731e800dedfc60fcb1d85b91a93cf688237e53d2f8920e4fad4ab047ee", value.BlockHash); + Assert.AreEqual("0dabde3e8b065e734247b7d5328ac18317af9842f0141ffe41173df15efd97a8", value.BlockHash); Assert.IsTrue(value.ExecutionResult.Effect.Count > 0); - Assert.AreEqual(1, value.Messages.Count); - var message = value.Messages[0]; - Assert.AreEqual("entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed", - message.AddressableEntity.ToString()); - Assert.IsNotEmpty(message.MessagePayload.String); - Assert.IsNull(message.MessagePayload.Bytes); - Assert.AreEqual("events", message.TopicName); - Assert.AreEqual("5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", message.TopicNameHash); - Assert.AreEqual(1, message.TopicIndex); - Assert.AreEqual(2, message.BlockIndex); + Assert.AreEqual(0, value.Messages.Count); + Assert.AreEqual("ApiError::Formatting [18]", value.ExecutionResult.ErrorMessage); } } } \ No newline at end of file From 3e820f4a4f78151785a58eefbefea14113c1146e Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 18 Dec 2024 12:40:31 +0100 Subject: [PATCH 104/126] Update 3.0.0-beta2 Changelog with changes introduced for RC5. Signed-off-by: David Hernando --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1426f68..a0f960d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,27 @@ All notable changes to this project will be documented in this file. The format [comment]: <> (Fixed: any bug fixes) [comment]: <> (Security: in case of vulnerabilities) +## [3.0.0-beta2] + +### Added + +* Added `GetDelegatorReward` and `GetValidatorReward` methods to the RPC client. [PR#76](https://github.com/make-software/casper-net-sdk/pull/76) and [PR#80](https://github.com/make-software/casper-net-sdk/pull/80). +* Added `GetStatePackage` method to the RPC client. [PR#82](https://github.com/make-software/casper-net-sdk/pull/82). +* Added `ProtocolVersion` to Get Node Status RPC response. [PR#77](https://github.com/make-software/casper-net-sdk/pull/77). +* Added a `TransactionBuilder` to build `TransactionV1` transactions for the different type of invocations ( to mint/auction system contracts, user stored contracts) and session deployments. [PR#79](https://github.com/make-software/casper-net-sdk/pull/79). + +### Changed + +* The `Transaction` type is now an abstraction for `TransactionV1` and `Deploy` transaction models. [PR#79](https://github.com/make-software/casper-net-sdk/pull/79). +* Updated `TransactionV1` JSON and bytes serialization in alignment to `casper-node v2.0.0-rc5`. [PR-84](https://github.com/make-software/casper-net-sdk/pull/84) and [PR-85](https://github.com/make-software/casper-net-sdk/pull/85) +* Updated `MessageKey` key type in alignment to `casper-node v2.0.0-rc5`. [PR-86](https://github.com/make-software/casper-net-sdk/pull/86) +* `EntryPointPayment.SelfOnly` constant replaced with `EntryPointPayment.DirectInvocationOnly`.[PR#87](https://github.com/make-software/casper-net-sdk/pull/87). + +### Fixed + +* Fixed a multi-thread bug when using the RPCLoggingHandler in the RPC client class. [PR#81](https://github.com/make-software/casper-net-sdk/pull/81). +* v3.0.0-beta1 did not parse correctly `Step` event emmitted by nodes in Casper v1.x. [PR#78](https://github.com/make-software/casper-net-sdk/pull/78). + ## [3.0.0-beta1] This version is compatible with Casper node v2.0.0-rc3 and Casper node v1.5.6. @@ -131,6 +152,7 @@ This new type permits to parse correctly the value `"00"` used for system blocks ### Added * Initial release of Casper .NET SDK. +[3.0.0-beta2]: https://github.com/make-software/casper-net-sdk/releases/tag/v3.0.0-beta2 [3.0.0-beta1]: https://github.com/make-software/casper-net-sdk/releases/tag/v3.0.0-beta1 [2.3.0]: https://github.com/make-software/casper-net-sdk/releases/tag/v2.3.0 [2.2.0]: https://github.com/make-software/casper-net-sdk/releases/tag/v2.2.0 From 49cf9c248ddfeec4ceee2776dcc85b68f7156752 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 18 Dec 2024 13:03:22 +0100 Subject: [PATCH 105/126] bugfix: Replace AddressableEntity with HashAddr in Message. Signed-off-by: David Hernando --- Casper.Network.SDK/Types/Message.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Casper.Network.SDK/Types/Message.cs b/Casper.Network.SDK/Types/Message.cs index bb2c3d4..14c23e5 100644 --- a/Casper.Network.SDK/Types/Message.cs +++ b/Casper.Network.SDK/Types/Message.cs @@ -44,9 +44,8 @@ public class Message /// /// The identity of the entity that produced the message. /// - [JsonPropertyName("entity_hash")] - [JsonConverter(typeof(AddressableEntityKey.AddressableEntityKeyConverter))] - public AddressableEntityKey AddressableEntity { get; init; } + [JsonPropertyName("hash_addr")] + public string HashAddr { get; init; } /// /// The payload of the message. From 971c8ab1a722d351621315df97d203647fd10db2 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 18 Dec 2024 13:08:23 +0100 Subject: [PATCH 106/126] update related test Signed-off-by: David Hernando --- Casper.Network.SDK.Test/SSETypesTest.cs | 8 +- .../sse-transaction-processed-v200.json | 160 +++--------------- 2 files changed, 26 insertions(+), 142 deletions(-) diff --git a/Casper.Network.SDK.Test/SSETypesTest.cs b/Casper.Network.SDK.Test/SSETypesTest.cs index 79b410f..20a71c0 100644 --- a/Casper.Network.SDK.Test/SSETypesTest.cs +++ b/Casper.Network.SDK.Test/SSETypesTest.cs @@ -83,14 +83,14 @@ public void TransactionProcessedTest() value.TransactionHash.Version1); Assert.AreEqual("01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", value.InitiatorAddr.PublicKey.ToString().ToLower()); - Assert.AreEqual("b3fc60731e800dedfc60fcb1d85b91a93cf688237e53d2f8920e4fad4ab047ee", value.BlockHash); + Assert.AreEqual("0dabde3e8b065e734247b7d5328ac18317af9842f0141ffe41173df15efd97a8", value.BlockHash); Assert.IsTrue(value.ExecutionResult.Effect.Count > 0); Assert.AreEqual(1, value.Messages.Count); var message = value.Messages[0]; - Assert.AreEqual("entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed", - message.AddressableEntity.ToString()); - Assert.IsNotEmpty(message.MessagePayload.String); + Assert.AreEqual("9038763d3066f0217047263ebd48dc7c839fadfdde141f5b990866563655b44a", + message.HashAddr); + Assert.AreEqual("dummy-data", message.MessagePayload.String); Assert.IsNull(message.MessagePayload.Bytes); Assert.AreEqual("events", message.TopicName); Assert.AreEqual("5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", message.TopicNameHash); diff --git a/Casper.Network.SDK.Test/TestData/sse-transaction-processed-v200.json b/Casper.Network.SDK.Test/TestData/sse-transaction-processed-v200.json index 96a5b0d..612c53c 100644 --- a/Casper.Network.SDK.Test/TestData/sse-transaction-processed-v200.json +++ b/Casper.Network.SDK.Test/TestData/sse-transaction-processed-v200.json @@ -5,180 +5,64 @@ "initiator_addr": { "PublicKey": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a" }, - "timestamp": "2024-07-01T15:24:47.381Z", + "timestamp": "2024-12-18T11:32:13.181Z", "ttl": "30m", - "block_hash": "b3fc60731e800dedfc60fcb1d85b91a93cf688237e53d2f8920e4fad4ab047ee", + "block_hash": "0dabde3e8b065e734247b7d5328ac18317af9842f0141ffe41173df15efd97a8", "execution_result": { "Version2": { "initiator": { "PublicKey": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a" }, - "error_message": null, - "limit": "100000000000", - "consumed": "520224703", - "cost": "100000000000", - "payment": [], + "error_message": "ApiError::Formatting [18]", + "limit": "3000000000", + "consumed": "1241925", + "cost": "3000000000", "transfers": [], - "size_estimate": 366, + "size_estimate": 591, "effects": [ { - "key": "balance-hold-01a9f3707fb752513760d089f4625a8727ba1e8b44880a8180e3780c29003742975c0ee66e90010000", + "key": "balance-hold-012cbc4e6c6edcb446920db63ec8c3d2e0e4e5922391ecf2a5e66d8208bbe394a473378ad993010000", "kind": { "Write": { "CLValue": { "cl_type": "U512", - "bytes": "0500e8764817", - "parsed": "100000000000" + "bytes": "04005ed0b2", + "parsed": "3000000000" } } } }, { - "key": "package-257b05da64d656592cde44bf3aa9f70f97b377b6342e016234d90aaaaaf0cfc5", - "kind": "Identity" - }, - { - "key": "entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed", - "kind": "Identity" - }, - { - "key": "package-257b05da64d656592cde44bf3aa9f70f97b377b6342e016234d90aaaaaf0cfc5", - "kind": "Identity" - }, - { - "key": "entry-point-v1-entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed-3820ce25e54df0470fb738e3e0f63ee50b2719cf2680da2bdb579e21aebc8f63", - "kind": "Identity" - }, - { - "key": "byte-code-v1-wasm-9e011083366d6dd0c32fafdf2b2ed5fa021b9c1c001af4c25b76715d6ef026af", - "kind": "Identity" - }, - { - "key": "dictionary-83e84f07db6e29c87ca51d44ac058f7ff61e0c1050eaf16110237b848048133f", - "kind": "Identity" - }, - { - "key": "dictionary-2d3fe9773442ce06ecb65e86a4a92d6818cb51cd6021218612a35250a7320298", - "kind": "Identity" - }, - { - "key": "dictionary-83e84f07db6e29c87ca51d44ac058f7ff61e0c1050eaf16110237b848048133f", - "kind": { - "Write": { - "CLValue": { - "cl_type": "Any", - "bytes": "0800000007aebcc5a47e8d0307200000002d5926cca6ebf4fd7d3555dca958f1fa3c6152418f89908515c062b61e7b665b30000000455147666f667749434e4f6c756571664f76544b6649773256566150337a654e69763334702b5675574b752f31413d3d", - "parsed": null - } - } - } - }, - { - "key": "dictionary-2d3fe9773442ce06ecb65e86a4a92d6818cb51cd6021218612a35250a7320298", - "kind": { - "Write": { - "CLValue": { - "cl_type": "Any", - "bytes": "030000000252c307200000002d5926cca6ebf4fd7d3555dca958f1fa3c6152418f89908515c062b61e7b665b300000004551482f716e4d7a46434865517631786f6f676b34574271484b663664552b52395148777a31594258334b457a513d3d", - "parsed": null - } - } - } - }, - { - "key": "uref-9561215a982d288e4d20426b9d45605b1642c7d0fe54c9f8632eb087b0a3dc7a-000", - "kind": "Identity" - }, - { - "key": "message-topic-entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", - "kind": "Identity" - }, - { - "key": "block-message-count-00000000000000000000000000000000000000000000000000000000000000", - "kind": "Identity" - }, - { - "key": "message-entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-0", - "kind": { - "Write": { - "Message": "message-checksum-4fa4135e65967751f007064a7cbf6c17d27f7189cc97b7b0f484445c72ecbd6f" - } - } - }, - { - "key": "message-topic-entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", - "kind": { - "Write": { - "MessageTopic": { - "message_count": 1, - "blocktime": 1719847489116 - } - } - } - }, - { - "key": "block-message-count-00000000000000000000000000000000000000000000000000000000000000", - "kind": { - "Write": { - "CLValue": { - "cl_type": { - "Tuple2": [ - "U64", - "U64" - ] - }, - "bytes": "5c0ee66e900100000100000000000000", - "parsed": [ - 1719847489116, - 1 - ] - } - } - } - }, - { - "key": "balance-hold-01a9f3707fb752513760d089f4625a8727ba1e8b44880a8180e3780c29003742975c0ee66e90010000", + "key": "balance-hold-012cbc4e6c6edcb446920db63ec8c3d2e0e4e5922391ecf2a5e66d8208bbe394a473378ad993010000", "kind": { - "Prune": "balance-hold-01a9f3707fb752513760d089f4625a8727ba1e8b44880a8180e3780c29003742975c0ee66e90010000" + "Prune": "balance-hold-012cbc4e6c6edcb446920db63ec8c3d2e0e4e5922391ecf2a5e66d8208bbe394a473378ad993010000" } }, { - "key": "balance-hold-00a9f3707fb752513760d089f4625a8727ba1e8b44880a8180e3780c29003742975c0ee66e90010000", + "key": "balance-hold-002cbc4e6c6edcb446920db63ec8c3d2e0e4e5922391ecf2a5e66d8208bbe394a473378ad993010000", "kind": { "Write": { "CLValue": { "cl_type": "U512", - "bytes": "0500e8764817", - "parsed": "100000000000" + "bytes": "04005ed0b2", + "parsed": "3000000000" } } } }, { - "key": "entity-system-5a28db419f9dfc65ad39f6c221a7801ff16ee15b6140a6ae7eb96903544b4928", - "kind": "Identity" - }, - { - "key": "entity-system-3d4d648d49f1840fdbf82cb41dbec8b64b164ed17dfe0fea0d444bdd44f688d3", - "kind": "Identity" - }, - { - "key": "entity-system-ba7e4fcf24bdc36fc932881f4989e7df2b542e8e58700765a8155903b9ced06b", - "kind": "Identity" - }, - { - "key": "bid-addr-01dcd0c38c46c9d5c7083aa1a46b430e8e460f97f8f0bf8444776ac925187acfcc", + "key": "bid-addr-01520037cd249ccbcfeb0b9feae07d8d4f7d922cf88adc4f3e8691f9d34ccc8d09", "kind": "Identity" }, { - "key": "bid-addr-04dcd0c38c46c9d5c7083aa1a46b430e8e460f97f8f0bf8444776ac925187acfcc0c00000000000000", + "key": "bid-addr-04520037cd249ccbcfeb0b9feae07d8d4f7d922cf88adc4f3e8691f9d34ccc8d093400000000000000", "kind": { "Write": { "BidKind": { "Credit": { - "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", - "era_id": 12, - "amount": "100000000000" + "validator_public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "era_id": 52, + "amount": "3000000000" } } } @@ -189,9 +73,9 @@ }, "messages": [ { - "entity_hash": "entity-contract-a9987538f3cceb823d627d6e28174fd7b50022c847db44b96c36077818e322ed", + "hash_addr": "9038763d3066f0217047263ebd48dc7c839fadfdde141f5b990866563655b44a", "message": { - "String": "Transfer(Transfer { sender: Key::AddressableEntity(account-9fa1fc0808d3a5b9ea9f3af4ca7c8c3655568fdf378d8afdf8a7e56e58abbfd4), recipient: Key::AddressableEntity(account-ffaa73331421de42fd71a28824e1606a1ca7fa754f91f501f0cf56015f7284cd), amount: 12502 })" + "String": "dummy-data" }, "topic_name": "events", "topic_name_hash": "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", From 48ad39406e69688c768a386b3b88a5c7e2073d3f Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 18 Dec 2024 16:06:10 +0100 Subject: [PATCH 107/126] bugfix with SeigniorageAllocation parsing Signed-off-by: David Hernando --- Casper.Network.SDK/Types/SeigniorageAllocation.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Casper.Network.SDK/Types/SeigniorageAllocation.cs b/Casper.Network.SDK/Types/SeigniorageAllocation.cs index 0734890..91d5eb3 100644 --- a/Casper.Network.SDK/Types/SeigniorageAllocation.cs +++ b/Casper.Network.SDK/Types/SeigniorageAllocation.cs @@ -62,7 +62,11 @@ public override SeigniorageAllocation Read( var field = reader.GetString(); reader.Read(); if (field == "delegator_public_key") - delegatorKind = JsonSerializer.Deserialize(ref reader, options); + { + delegatorKind = reader.TokenType == JsonTokenType.String + ? new DelegatorKind() { PublicKey = PublicKey.FromHexString(reader.GetString()) } + : JsonSerializer.Deserialize(ref reader, options); + } else if (field == "validator_public_key") validatorPk = reader.GetString(); else if (field == "amount") From 3738240651c9de82f9d553f3a7d51db539dfe847 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 18 Dec 2024 18:43:20 +0100 Subject: [PATCH 108/126] fix seigniorage allocation parsing. Added unit tests for v158 and v200. Signed-off-by: David Hernando --- Casper.Network.SDK.Test/StoredValueTest.cs | 44 +++++ .../TestData/seigniorage_allocation_v158.json | 71 ++++++++ .../TestData/seigniorage_allocation_v200.json | 156 ++++++++++++++++++ .../Types/SeigniorageAllocation.cs | 10 +- 4 files changed, 275 insertions(+), 6 deletions(-) create mode 100644 Casper.Network.SDK.Test/StoredValueTest.cs create mode 100644 Casper.Network.SDK.Test/TestData/seigniorage_allocation_v158.json create mode 100644 Casper.Network.SDK.Test/TestData/seigniorage_allocation_v200.json diff --git a/Casper.Network.SDK.Test/StoredValueTest.cs b/Casper.Network.SDK.Test/StoredValueTest.cs new file mode 100644 index 0000000..0954e44 --- /dev/null +++ b/Casper.Network.SDK.Test/StoredValueTest.cs @@ -0,0 +1,44 @@ +using System.IO; +using Casper.Network.SDK.Types; +using NUnit.Framework; +using JsonSerializer = System.Text.Json.JsonSerializer; + +namespace NetCasperTest +{ + public class StoredValueTest + { + [Test] + public void SeigniorageAllocationsTest_V200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/seigniorage_allocation_v200.json"); + + var storedValue = JsonSerializer.Deserialize(json); + Assert.IsNotNull(storedValue); + Assert.IsNotNull(storedValue.EraInfo); + Assert.AreEqual(20, storedValue.EraInfo.SeigniorageAllocations.Count); + Assert.IsTrue(storedValue.EraInfo.SeigniorageAllocations[0].IsDelegator); + Assert.AreEqual("018b46617b2b97e633b36530f2964b3f4c15916235910a2737e83d4fa2c7fad542", storedValue.EraInfo.SeigniorageAllocations[0].DelegatorKind.PublicKey.ToString().ToLower()); + Assert.AreEqual("01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", storedValue.EraInfo.SeigniorageAllocations[0].ValidatorPublicKey.ToString().ToLower()); + Assert.AreEqual("2515330120214391", storedValue.EraInfo.SeigniorageAllocations[0].Amount.ToString()); + Assert.IsFalse(storedValue.EraInfo.SeigniorageAllocations[1].IsDelegator); + } + + [Test] + public void SeigniorageAllocationsTest_V158() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/seigniorage_allocation_v158.json"); + + var storedValue = JsonSerializer.Deserialize(json); + Assert.IsNotNull(storedValue); + Assert.IsNotNull(storedValue.EraInfo); + Assert.AreEqual(10, storedValue.EraInfo.SeigniorageAllocations.Count); + Assert.IsTrue(storedValue.EraInfo.SeigniorageAllocations[0].IsDelegator); + Assert.AreEqual("018b46617b2b97e633b36530f2964b3f4c15916235910a2737e83d4fa2c7fad542", storedValue.EraInfo.SeigniorageAllocations[0].DelegatorKind.PublicKey.ToString().ToLower()); + Assert.AreEqual("01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", storedValue.EraInfo.SeigniorageAllocations[0].ValidatorPublicKey.ToString().ToLower()); + Assert.AreEqual("4414692857142857", storedValue.EraInfo.SeigniorageAllocations[0].Amount.ToString()); + Assert.IsFalse(storedValue.EraInfo.SeigniorageAllocations[1].IsDelegator); + } + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/seigniorage_allocation_v158.json b/Casper.Network.SDK.Test/TestData/seigniorage_allocation_v158.json new file mode 100644 index 0000000..bb0b1af --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/seigniorage_allocation_v158.json @@ -0,0 +1,71 @@ +{ + "EraInfo": { + "seigniorage_allocations": [ + { + "Delegator": { + "delegator_public_key": "018b46617b2b97e633b36530f2964b3f4c15916235910a2737e83d4fa2c7fad542", + "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "amount": "4414692857142857" + } + }, + { + "Validator": { + "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "amount": "4585307142857143" + } + }, + { + "Delegator": { + "delegator_public_key": "0197b79d1a1351f8fb922b9f7f556d2bbfdba5105df9eaa6caa07804c703a641ed", + "validator_public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "amount": "4414692857142857" + } + }, + { + "Validator": { + "validator_public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "amount": "4585307142857143" + } + }, + { + "Delegator": { + "delegator_public_key": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", + "validator_public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "amount": "4414692857142857" + } + }, + { + "Validator": { + "validator_public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "amount": "4585307142857143" + } + }, + { + "Delegator": { + "delegator_public_key": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", + "validator_public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "amount": "4414692857142857" + } + }, + { + "Validator": { + "validator_public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "amount": "4585307142857143" + } + }, + { + "Delegator": { + "delegator_public_key": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55", + "validator_public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "amount": "4414692857142857" + } + }, + { + "Validator": { + "validator_public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "amount": "4585307142857143" + } + } + ] + } +} \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/seigniorage_allocation_v200.json b/Casper.Network.SDK.Test/TestData/seigniorage_allocation_v200.json new file mode 100644 index 0000000..6b27c35 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/seigniorage_allocation_v200.json @@ -0,0 +1,156 @@ +{ + "EraInfo": { + "seigniorage_allocations": [ + { + "Delegator": { + "delegator_kind": { + "PublicKey": "018b46617b2b97e633b36530f2964b3f4c15916235910a2737e83d4fa2c7fad542" + }, + "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "amount": "2515330120214391" + } + }, + { + "Validator": { + "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "amount": "2728720430156545" + } + }, + { + "Delegator": { + "delegator_kind": { + "PublicKey": "018b46617b2b97e633b36530f2964b3f4c15916235910a2737e83d4fa2c7fad542" + }, + "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "amount": "109303520813010" + } + }, + { + "Validator": { + "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", + "amount": "118554941151112" + } + }, + { + "Delegator": { + "delegator_kind": { + "PublicKey": "0197b79d1a1351f8fb922b9f7f556d2bbfdba5105df9eaa6caa07804c703a641ed" + }, + "validator_public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "amount": "8599696498056110" + } + }, + { + "Validator": { + "validator_public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "amount": "9377950843219784" + } + }, + { + "Delegator": { + "delegator_kind": { + "PublicKey": "0197b79d1a1351f8fb922b9f7f556d2bbfdba5105df9eaa6caa07804c703a641ed" + }, + "validator_public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "amount": "285067736921916" + } + }, + { + "Validator": { + "validator_public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "amount": "310701366981535" + } + }, + { + "Delegator": { + "delegator_kind": { + "PublicKey": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a" + }, + "validator_public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "amount": "5976757455713484" + } + }, + { + "Validator": { + "validator_public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "amount": "6492754998004464" + } + }, + { + "Delegator": { + "delegator_kind": { + "PublicKey": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a" + }, + "validator_public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "amount": "162277940193805" + } + }, + { + "Validator": { + "validator_public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "amount": "176125500882714" + } + }, + { + "Delegator": { + "delegator_kind": { + "PublicKey": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4" + }, + "validator_public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "amount": "6111063397723576" + } + }, + { + "Validator": { + "validator_public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "amount": "6660504858490961" + } + }, + { + "Delegator": { + "delegator_kind": { + "PublicKey": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4" + }, + "validator_public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "amount": "183204228041446" + } + }, + { + "Validator": { + "validator_public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "amount": "199637730476608" + } + }, + { + "Delegator": { + "delegator_kind": { + "PublicKey": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55" + }, + "validator_public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "amount": "2170319328593039" + } + }, + { + "Validator": { + "validator_public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "amount": "2366902069827651" + } + }, + { + "Delegator": { + "delegator_kind": { + "PublicKey": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55" + }, + "validator_public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "amount": "217749920954248" + } + }, + { + "Validator": { + "validator_public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "amount": "237377113583604" + } + } + ] + } +} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/SeigniorageAllocation.cs b/Casper.Network.SDK/Types/SeigniorageAllocation.cs index 91d5eb3..77fbcdb 100644 --- a/Casper.Network.SDK/Types/SeigniorageAllocation.cs +++ b/Casper.Network.SDK/Types/SeigniorageAllocation.cs @@ -61,12 +61,10 @@ public override SeigniorageAllocation Read( { var field = reader.GetString(); reader.Read(); - if (field == "delegator_public_key") - { - delegatorKind = reader.TokenType == JsonTokenType.String - ? new DelegatorKind() { PublicKey = PublicKey.FromHexString(reader.GetString()) } - : JsonSerializer.Deserialize(ref reader, options); - } + if (field == "delegator_kind") + delegatorKind = JsonSerializer.Deserialize(ref reader, options); + else if (field == "delegator_public_key") + delegatorKind = new DelegatorKind() { PublicKey = PublicKey.FromHexString(reader.GetString()) }; else if (field == "validator_public_key") validatorPk = reader.GetString(); else if (field == "amount") From 3bb2090d34b6fc3d584f80af28797ce44e81282d Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 18 Dec 2024 19:02:03 +0100 Subject: [PATCH 109/126] Documentation fixes for beta2. Bump up BouncyCastle version Signed-off-by: David Hernando --- Casper.Network.SDK/Casper.Network.SDK.csproj | 8 +-- Casper.Network.SDK/JsonRpc/CasperMethods.cs | 2 +- Casper.Network.SDK/NetCasperClient.cs | 4 +- Casper.Network.SDK/SSE/ServerEventsClient.cs | 1 + .../Types/TransactionBuilder.cs | 1 - ...tionGuide.md => Casper20MigrationGuide.md} | 60 +++++-------------- 6 files changed, 22 insertions(+), 54 deletions(-) rename Docs/Articles/{CondorMigrationGuide.md => Casper20MigrationGuide.md} (66%) diff --git a/Casper.Network.SDK/Casper.Network.SDK.csproj b/Casper.Network.SDK/Casper.Network.SDK.csproj index d7f9ce7..fc74d25 100644 --- a/Casper.Network.SDK/Casper.Network.SDK.csproj +++ b/Casper.Network.SDK/Casper.Network.SDK.csproj @@ -5,8 +5,8 @@ 9.0 CS1591 3.0.0.0 - 3.0.0-beta1 - 3.0.0-beta1 + 3.0.0-beta2 + 3.0.0-beta2 Casper.Network.SDK make-software https://github.com/make-software/casper-net-sdk @@ -24,8 +24,8 @@ - - + + diff --git a/Casper.Network.SDK/JsonRpc/CasperMethods.cs b/Casper.Network.SDK/JsonRpc/CasperMethods.cs index de96560..e8cae22 100644 --- a/Casper.Network.SDK/JsonRpc/CasperMethods.cs +++ b/Casper.Network.SDK/JsonRpc/CasperMethods.cs @@ -234,7 +234,7 @@ public class QueryBalance : RpcMethod /// Query for balance information using a purse identifier and a state identifier /// /// The identifier to obtain the purse corresponding to balance query. - /// The identifier for the state used for the query, if none is passed, the latest block will be used. + /// The identifier for the state used for the query, if none is passed, the latest block will be used. public QueryBalance(IPurseIdentifier purseIdentifier, StateIdentifier stateIdentifier = null) : base("query_balance") { this.Parameters = new Dictionary diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index 00bfb38..bef3827 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -225,7 +225,7 @@ public async Task> GetEntity(string entityAddr, ulo /// /// Returns a Package from the network /// - /// The entity address to get information of. + /// The entity address to get information of. /// A block hash for which the information of the entity is queried. Null for most recent information. public async Task> GetPackage(string packageHash, string blockHash = null) { @@ -239,7 +239,7 @@ public async Task> GetPackage(string packageHash, /// /// Returns a Package from the network /// - /// The package address or contract package hash to get information of. + /// The package address or contract package hash to get information of. /// A block height for which the information of the package is queried. public async Task> GetPackage(string packageHash, ulong blockHeight) { diff --git a/Casper.Network.SDK/SSE/ServerEventsClient.cs b/Casper.Network.SDK/SSE/ServerEventsClient.cs index 0667f64..c1bbee3 100644 --- a/Casper.Network.SDK/SSE/ServerEventsClient.cs +++ b/Casper.Network.SDK/SSE/ServerEventsClient.cs @@ -142,6 +142,7 @@ public ServerEventsClient() /// /// IP or domain name of the node. /// Event stream port. + /// 2 for Casper 2.x; 1 for Casper 1.x. public ServerEventsClient(string host, int port, int nodeVersion = 2) : this() { _host = host; diff --git a/Casper.Network.SDK/Types/TransactionBuilder.cs b/Casper.Network.SDK/Types/TransactionBuilder.cs index 8ce07cb..008f305 100644 --- a/Casper.Network.SDK/Types/TransactionBuilder.cs +++ b/Casper.Network.SDK/Types/TransactionBuilder.cs @@ -603,7 +603,6 @@ public ContractCallBuilder RuntimeArgs(List args) public class SessionBuilder : TransactionV1Builder { private bool _isInstallOrUpgrade = false; - private byte[] _wasm = null; public SessionBuilder() { diff --git a/Docs/Articles/CondorMigrationGuide.md b/Docs/Articles/Casper20MigrationGuide.md similarity index 66% rename from Docs/Articles/CondorMigrationGuide.md rename to Docs/Articles/Casper20MigrationGuide.md index 0c7cc7e..c07b754 100644 --- a/Docs/Articles/CondorMigrationGuide.md +++ b/Docs/Articles/Casper20MigrationGuide.md @@ -1,12 +1,12 @@ # Migration from Casper .NET SDK v2.x to v3.0 -To migrate your application from Casper .NET SDK v2.x to Casper .NET SDK v3.0, you’ll need to make several changes to your code. The good news is that version 3 of this SDK keeps compatibility with Casper v1.5.6 nodes; therefore, once you update your application, it will work before and after the Condor upgrade. +To migrate your application from Casper .NET SDK v2.x to Casper .NET SDK v3.0, you’ll need to make several changes to your code. The good news is that version 3 of this SDK keeps compatibility with Casper v1.5.6 nodes; therefore, once you update your application, it will work before and after the Casper 2.0 upgrade. -This guide outlines the changes necessary for your application. However, it's worth noting that it doesn't replace other Condor-related documents introducing the new concepts in Casper 2.0 or migration guides. We strongly advise the reader to understand these new concepts first, as they will significantly aid in grasping the changes. +This guide outlines the changes necessary for your application. However, it's worth noting that it doesn't replace other documents introducing the new concepts in Casper 2.0 or migration guides. We strongly advise the reader to understand these new concepts first, as they will significantly aid in grasping the changes. ## Blocks -With Condor, produced blocks are stored in a new format that extends the information contained compared to old blocks. Blocks produced before the upgrade keep their original format. Thus, the SDK implements `BlockV1` and `BlockV2` classes to handle old and new block formats, respectively. +With Casper 2.0, produced blocks are stored in a new format that extends the information contained compared to old blocks. Blocks produced before the upgrade keep their original format. Thus, the SDK implements `BlockV1` and `BlockV2` classes to handle old and new block formats, respectively. To facilitate handling different versions, the SDK also implements the type `Block`, which can represent either a V1 or V2 type in the network. This is the type obtained by default in the RPC queries and the SSE channel and contains all the data you may want to query: @@ -34,7 +34,7 @@ public class Block Note that `Block` does not have a header or body parts. -Also, some properties may have a `null` value if they’re not part of the versioned block. For example, `LastSwitchBlockHash` is present only for V2 blocks and `null` for V1 blocks. +Also, some properties may have a `null` value if they’re not part of the versioned block version. For example, `LastSwitchBlockHash` is present only for V2 blocks and `null` for V1 blocks. ### Recovering the versioned block object @@ -52,7 +52,7 @@ if (block.Version == 2) { ### EraEnd -EraEnd structure also differs for switch blocks produced before and after Condor. In most cases, you can just use the EraEnd object from the common Block class. But again, if necessary, you can recover an EraEndV1 object from a Version 1 Block instance: +EraEnd structure also differs for switch blocks produced before and after Casper 2.0. In most cases, you can just use the EraEnd object from the common Block class. But again, if necessary, you can recover an EraEndV1 object from a version 1 block instance: ```csharp if (block.Version == 1) { @@ -64,7 +64,7 @@ if (block.Version == 1) { ### Transactions included in a block -Blocks produced on Casper v1.x contain a list of deploys that differentiate between native transfers and all other types of deploys. On Condor, transactions (this includes legacy deploys too) have a category field that determines the processing lane to which the transaction is sent. The chainspec of the network determines the properties of each lane: +Blocks produced on Casper v1.x contain a list of deploys that differentiate between native transfers and all other types of deploys. On Casper 2.0, transactions (this includes legacy deploys too) have a category field that determines the processing lane to which the transaction is sent. The chainspec of the network determines the properties of each lane: 1. Maximum transaction size in bytes for a given transaction in a certain lane. 2. Maximum args length size in bytes for a given transaction in a certain lane. @@ -88,7 +88,7 @@ public List Transactions { get; init; } The category, version (either a legacy Deploy or the new TransactionV1 type), and the hash are provided for each transaction. -`Deploy`s in V1 blocks are categorized as `Mint` for native transfer deploys and `Large` for all the rest. The same happens for legacy deploys sent to a Casper v2.x node. +`Deploy`s in V1 blocks are categorized as `Mint` for native transfer deploys and `Large` for all the rest. The same applies to legacy deploys sent to a Casper v2.x node. ### Block height type @@ -113,41 +113,9 @@ foreach(var transfer in result.Transfers) } ``` -## Account/Contract Merge - -On Condor, accounts and contracts are stored with the new type AddressableEntity. The EntityKind property in this type permits knowing whether the record is an Account, a stored SmartContract, or a System contract. - -### GetEntity RPC method - -Use the new method GetEntity(IEntityIdentifier) in the RPC interface to retrieve a record. PublicKey, AccountHashKey, and AddressableEntityKey implement the IEntityIdentiifer interface and can be used to retrieve an AddressableEntity from the network. While PublicKey and AccountHashKeyare known types from Casper v1.x, the AddressableEntitykey is new, but the developer must become familiar with it to work with Condor. Some examples of this key are: - -``` -entity-account-2f3fb80d362ad0a922f446915a259c9aaec9ba99292b3e50ff2359c458007309 -entity-contract-a5cf5917505ef60a6f0df395dd19e86a0f075d00f2e6ce49f5aa0e18f6e26f5d -entity-system-a1b5f200a58533875ef83cb98de14f128342b34162cbc14d4f41f3ccbc451dc3 -``` - -### Legacy accounts - -Existing accounts are migrated either during the Condor upgrade or when they interact with the network for the first time after the upgrade. The exact time depends on the `chainspec` agreed for the Condor upgrade. Hash values for the `AccountHashKey` and the `EntityKey` are shared and remain unchanged for migrated accounts. - -Non-migrated accounts can also be retrieved using the new `GetEntity` method. In this case, the response contains the account info in the `LegacyAccount` property instead of the `Entity` property. - -Alternatively, the legacy method `GetAccountInfo` also works for non-migrated accounts. - -### Contract information - -Like accounts, contract records have also been migrated to the new `AddressableEntity`. After the migration, only the `GetEntity` method can be used to retrieve contract information. Hash values for contracts and packages remain unchanged for the migrated records. - -To retrieve information about the contract package, use the `QueryGlobalState` method with the `PackageKey` of the contract. - -### Backwards compatibility - -When using the SDK v3 with a Casper v1.x network, only GetAccountInfo can be used to retrieve account information. For contract and package information use QueryGlobalState. - ## Balances -The new `NoFee` mode in Condor introduces the concept of a ‘balance hold’. In this mode of operation, for each transaction, the network holds the amount paid for its execution in the paying purse. Thus, entities have a total balance and an available balance, where the available balance is equal to the total balance minus the balance holds. +The new `NoFee` mode in Casper 2.0 introduces the concept of a ‘balance hold’. In this mode of operation, for each transaction, the network holds the amount paid for its execution in the paying purse. Thus, entities have a total balance and an available balance, where the available balance is equal to the total balance minus the held balance. To get detailed information on an entity balance, use the new method `QueryBalanceDetails(IPurseIdentifier)` with a purse identifier. `PublicKey`, `AccountHashKey`, `AddressableEntityKey`, and `URef` keys implement the `IPurseIdentifier` interface and can be used to retrieve the balance details for an entity. @@ -157,9 +125,9 @@ To get detailed information on an entity balance, use the new method `QueryBalan Condor introduces a new transaction model to support advanced use cases. While `Deploy`s continue to work in Condor, this type is deprecated, and Casper recommends switching to the new `TransactionV1` type. -Similar to the `DeployTemplates` class, which provides deploy templates for most common use cases, we plan to implement a TransactionV1Templates class for the new transaction model in the next release of this SDK. +Similar to the `DeployTemplates` class, which provides deploy templates for most common use cases, we've implemented a `TransactionBuilder` class to facilitate the creation of transactions using the new `TransactionV1` model for different use cases. Check the documentation and the examples for more information. -Use the new method `PutTransaction` to send a `TransactionV1` to the network. Use the new `GetTransaction` method to retrieve an accepted transaction. Both methods can also be used to send and retrieve a Deploy. For unprocessed transactions, the ExecutionInfo in the response is `null`. Upon processing, this property contains all information about the execution, including cost, payments, errors (if any), and execution effects. +Use the new method `PutTransaction` to send a `TransactionV1` to the network. Use the new `GetTransaction` method to retrieve an accepted transaction. Both methods can also be used to send and retrieve a `Deploy`. For unprocessed transactions, the ExecutionInfo in the response is `null`. Upon processing, this property contains all information about the execution, including cost, payments, errors (if any), and execution effects. ### GetDeploy() @@ -180,7 +148,7 @@ In the NoFee model, the user does not specify any amount for payment. Instead, h The `ExecutionResult` class is a versioned object. Deploys processed before the Condor upgrade are stored in the global state as `ExecutionResultV1`records, and deploys and transactions processed after the Condor upgrade are stored as `ExecutionResultV2` records. -For a processed transaction, the `GetTransaction` method always returns an `ExecutionResult` instance regardless of the version. It has the same fields as `ExecutionResultV2`. The user can obtain the original structure by casting this instance to the correct version: +For a processed transaction, the `GetTransaction` method always returns an `ExecutionResult` instance regardless of the version. It has the same fields as `ExecutionResultV2`. You can obtain the original structure by casting this instance to the correct version: ```csharp if (executionResult.Version == 2) { @@ -196,11 +164,11 @@ The Effect property contains a list of Transforms that modify the global state d ## Auction contract -The auction contract also has changed in Condor. If your application tracks validator bids, rewards, and delegators, you must rework how network responses are parsed and interpreted. A complete description of the changes cannot covered in this guide. +The auction contract has also changed in Condor. If your application tracks validator bids, rewards, and delegators, you must rework how network responses are parsed and interpreted. A complete description of the changes cannot be covered in this guide. ## Server Sent Events -The ServerEventsClient class can listen to an event stream from a v1.x node and from v2.x. +The ServerEventsClient class listens to an event stream from a v1.x node and from v2.x. By default, the client class expects a 2.x network. To listen to a 1.x network change the `nodeVersion` property to `1`. ### Blocks @@ -216,7 +184,7 @@ A `TransactionAccepted` event contains a `Transaction` property with either a `D The `FinalitySignature` event contains an instance of the versioned `FinalitySignature` class. Version 2 of this type is an extension that contains all properties in version 1 plus the block height and the chain name hash. -The user can obtain the original structure by casting this instance to the correct version: +The user can obtain the original structure by casting this instance to the appropriate version: ```csharp if (finalitySignature.Version == 2) { From adfbe9e62148091258a6d94bf4f8d423c5046f6d Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 20 Dec 2024 15:52:37 +0100 Subject: [PATCH 110/126] return Transaction object in TransactionBuilder class. Signed-off-by: David Hernando --- Casper.Network.SDK/Types/Transaction.cs | 12 +++++++++ .../Types/TransactionBuilder.cs | 26 +++++++++---------- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Casper.Network.SDK/Types/Transaction.cs b/Casper.Network.SDK/Types/Transaction.cs index 42d2f69..8ed6884 100644 --- a/Casper.Network.SDK/Types/Transaction.cs +++ b/Casper.Network.SDK/Types/Transaction.cs @@ -115,6 +115,18 @@ public class SessionTransactionInvocation : TransactionInvocation public byte[] Wasm { get; init; } } + /// + /// Signs the transaction with a private key and adds a new Approval to it. + /// + public void Sign(KeyPair keyPair) + { + if(_deploy is not null) + _deploy.Sign(keyPair); + + if(_transactionV1 is not null) + _transactionV1.Sign(keyPair); + } + public class TransactionConverter : JsonConverter { public override Transaction Read( diff --git a/Casper.Network.SDK/Types/TransactionBuilder.cs b/Casper.Network.SDK/Types/TransactionBuilder.cs index 008f305..858887a 100644 --- a/Casper.Network.SDK/Types/TransactionBuilder.cs +++ b/Casper.Network.SDK/Types/TransactionBuilder.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Numerics; using Casper.Network.SDK.Utils; -using Org.BouncyCastle.Utilities.Encoders; namespace Casper.Network.SDK.Types { @@ -82,7 +81,7 @@ protected void ValidateRequiredProperties() } } - public virtual TransactionV1 Build() + public virtual Transaction Build() { var payload = new TransactionV1Payload() { @@ -96,7 +95,8 @@ public virtual TransactionV1 Build() EntryPoint = _entryPoint, Scheduling = _scheduling, }; - return new TransactionV1(payload); + var transactionV1 = new TransactionV1(payload); + return (Transaction)transactionV1; } } @@ -145,7 +145,7 @@ public NativeTransferBuilder Id(ulong id) return this; } - public override TransactionV1 Build() + public override Transaction Build() { ValidateRequiredProperties(); @@ -222,7 +222,7 @@ public NativeAddBidBuilder ReservedSlots(uint reservedSlots) return this; } - public override TransactionV1 Build() + public override Transaction Build() { ValidateRequiredProperties(); @@ -272,7 +272,7 @@ public NativeWithdrawBidBuilder Amount(BigInteger amount) return this; } - public override TransactionV1 Build() + public override Transaction Build() { ValidateRequiredProperties(); @@ -315,7 +315,7 @@ public NativeDelegateBuilder Amount(BigInteger amount) return this; } - public override TransactionV1 Build() + public override Transaction Build() { ValidateRequiredProperties(); @@ -360,7 +360,7 @@ public NativeUndelegateBuilder Amount(BigInteger amount) return this; } - public override TransactionV1 Build() + public override Transaction Build() { ValidateRequiredProperties(); @@ -413,7 +413,7 @@ public NativeRedelegateBuilder Amount(BigInteger amount) return this; } - public override TransactionV1 Build() + public override Transaction Build() { ValidateRequiredProperties(); @@ -445,7 +445,7 @@ public NativeActivateBidBuilder Validator(PublicKey publicKey) return this; } - public override TransactionV1 Build() + public override Transaction Build() { ValidateRequiredProperties(); @@ -481,7 +481,7 @@ public NativeChangeBidPublicKeyBuilder NewPublicKey(PublicKey publicKey) return this; } - public override TransactionV1 Build() + public override Transaction Build() { ValidateRequiredProperties(); @@ -510,7 +510,7 @@ public NativeAddReservationsBuilder Reservations(List reservations) return this; } - public override TransactionV1 Build() + public override Transaction Build() { ValidateRequiredProperties(); @@ -546,7 +546,7 @@ public NativeCancelReservationsBuilder Delegators(List delegators return this; } - public override TransactionV1 Build() + public override Transaction Build() { ValidateRequiredProperties(); From 2e45527481d6e1f1367728b2e807402fcf905358 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Fri, 20 Dec 2024 15:56:33 +0100 Subject: [PATCH 111/126] bump up SDK version. Signed-off-by: David Hernando --- Casper.Network.SDK/Casper.Network.SDK.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Casper.Network.SDK/Casper.Network.SDK.csproj b/Casper.Network.SDK/Casper.Network.SDK.csproj index fc74d25..26645a7 100644 --- a/Casper.Network.SDK/Casper.Network.SDK.csproj +++ b/Casper.Network.SDK/Casper.Network.SDK.csproj @@ -5,8 +5,8 @@ 9.0 CS1591 3.0.0.0 - 3.0.0-beta2 - 3.0.0-beta2 + 3.0.1-beta2 + 3.0.1-beta2 Casper.Network.SDK make-software https://github.com/make-software/casper-net-sdk From c8fd4d93ff4cedd64f6db30553300aa9dc08f970 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 4 Feb 2025 10:25:59 +0100 Subject: [PATCH 112/126] Add get auction info v2 support. Signed-off-by: David Hernando --- .../RPCResponses/GetAuctionInfoResultTest.cs | 47 +- .../TestData/get-auction-info-v200.json | 413 ++++-------------- .../Converters/BidsListConverter.cs | 65 ++- .../Converters/NamedArgsListConverter.cs | 14 +- Casper.Network.SDK/JsonRpc/CasperMethods.cs | 19 + Casper.Network.SDK/NetCasperClient.cs | 40 +- Casper.Network.SDK/Types/AuctionState.cs | 6 +- 7 files changed, 216 insertions(+), 388 deletions(-) diff --git a/Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs index df8a415..cf88bb6 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs @@ -28,11 +28,11 @@ public void GetBlockResultTest_v156() Assert.AreEqual(BigInteger.Parse("10567495110201092"), result.AuctionState.EraValidators[0].ValidatorWeights[4].Weight); Assert.AreEqual(3, result.AuctionState.Bids.Count); - Assert.AreEqual("01001b79b9a6e13d2b96e916f7fa7dff40496ba5188479263ca0fb2ccf8b714305", result.AuctionState.Bids[0].PublicKey.ToString().ToLower()); - Assert.AreEqual(1, result.AuctionState.Bids[0].Delegators.Count); - Assert.AreEqual("018b34b15e023844531621cb52d42e216a2ea56034f0f40bf7cee566c32eae4f83", result.AuctionState.Bids[0].Delegators[0].DelegatorPublicKey.ToString().ToLower()); - Assert.AreEqual(BigInteger.Parse("30268476029"), result.AuctionState.Bids[0].Delegators[0].StakedAmount); - Assert.IsNull(result.AuctionState.Bids[0].Delegators[0].VestingSchedule); + Assert.AreEqual("01001b79b9a6e13d2b96e916f7fa7dff40496ba5188479263ca0fb2ccf8b714305", result.AuctionState.Bids[0].Unified.PublicKey.ToString().ToLower()); + Assert.AreEqual(1, result.AuctionState.Bids[0].Unified.Delegators.Count); + Assert.AreEqual("018b34b15e023844531621cb52d42e216a2ea56034f0f40bf7cee566c32eae4f83", result.AuctionState.Bids[0].Unified.Delegators[0].DelegatorPublicKey.ToString().ToLower()); + Assert.AreEqual(BigInteger.Parse("30268476029"), result.AuctionState.Bids[0].Unified.Delegators[0].StakedAmount); + Assert.IsNull(result.AuctionState.Bids[0].Unified.Delegators[0].VestingSchedule); } [Test] @@ -44,21 +44,32 @@ public void GetBlockResultTest_v200() var result = RpcResult.Parse(json); Assert.IsNotNull(result); Assert.AreEqual("2.0.0", result.ApiVersion); - Assert.AreEqual("1e8f22fb799932c56ffcf4d48c014e09ba9b791a3280f9a8cc9c7614ce7d562e", result.AuctionState.StateRootHash); - Assert.AreEqual(1394, result.AuctionState.BlockHeight); - Assert.AreEqual(1394, result.AuctionState.BlockHeight); + Assert.AreEqual("c8228d6d6d45151766901ba3579461847a17db15a66ce9ef6ae2f3e3abffd132", result.AuctionState.StateRootHash); + Assert.AreEqual(3480973, result.AuctionState.BlockHeight); Assert.AreEqual(3, result.AuctionState.EraValidators.Count); - Assert.AreEqual(128, result.AuctionState.EraValidators[2].EraId); - Assert.AreEqual(5, result.AuctionState.EraValidators[2].ValidatorWeights.Count); - Assert.AreEqual("01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", result.AuctionState.EraValidators[2].ValidatorWeights[4].PublicKey.ToString().ToLower()); - Assert.AreEqual(BigInteger.Parse("1366181433007372460"), result.AuctionState.EraValidators[2].ValidatorWeights[4].Weight); + Assert.AreEqual(14912, result.AuctionState.EraValidators[2].EraId); + Assert.AreEqual(4, result.AuctionState.EraValidators[2].ValidatorWeights.Count); + Assert.AreEqual("017536433a73f7562526f3e9fcb8d720428ae2d28788a9909f3c6f637a9d848a4b", result.AuctionState.EraValidators[2].ValidatorWeights[3].PublicKey.ToString().ToLower()); + Assert.AreEqual(BigInteger.Parse("2030445261010189498"), result.AuctionState.EraValidators[2].ValidatorWeights[3].Weight); - Assert.AreEqual(5, result.AuctionState.Bids.Count); - Assert.AreEqual("01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", result.AuctionState.Bids[4].PublicKey.ToString().ToLower()); - Assert.AreEqual(3, result.AuctionState.Bids[4].Delegators.Count); - Assert.AreEqual("0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55", result.AuctionState.Bids[4].Delegators[1].DelegatorPublicKey.ToString().ToLower()); - Assert.AreEqual(BigInteger.Parse("654063155243124749"), result.AuctionState.Bids[4].Delegators[1].StakedAmount); - Assert.AreEqual(1719301233872, result.AuctionState.Bids[4].Delegators[1].VestingSchedule.InitialReleaseTimestampMillis); + Assert.AreEqual(4, result.AuctionState.Bids.Count); + + Assert.IsNotNull(result.AuctionState.Bids[0].Validator); + Assert.AreEqual("01358a7e107668ae2eb092dcfbeb97d2ec3cc8354d2a77bc8f232fff6630a826c3", result.AuctionState.Bids[0].Validator.PublicKey.ToString().ToLower()); + Assert.AreEqual("uref-027d909fa0818f8b426b905795f608a6301168476b8013d7b3f682786796096f-007", result.AuctionState.Bids[0].Validator.BondingPurse.ToString()); + Assert.AreEqual(500000000000, result.AuctionState.Bids[0].Validator.MinimumDelegationAmount); + Assert.AreEqual(1000000000000000000, result.AuctionState.Bids[0].Validator.MaximumDelegationAmount); + Assert.AreEqual(3, result.AuctionState.Bids[0].Validator.ReservedSlots); + Assert.AreEqual(1, result.AuctionState.Bids[0].Validator.DelegationRate); + Assert.AreEqual(BigInteger.Parse("2500000000"), result.AuctionState.Bids[0].Validator.StakedAmount); + Assert.AreEqual(true, result.AuctionState.Bids[0].Validator.Inactive); + + Assert.IsNotNull(result.AuctionState.Bids[3].Delegator); + Assert.AreEqual("01032146b0b9de01e26aaec7b0d1769920de94681dbd432c3530bfe591752ded6c", result.AuctionState.Bids[3].Delegator.ValidatorPublicKey.ToString().ToLower()); + Assert.AreEqual("uref-d34ee21f5fe61feee2d9f15e0e369367aba62ba1b689e59c1e6ddc581ca99fdf-007", result.AuctionState.Bids[3].Delegator.BondingPurse.ToString()); + Assert.AreEqual(BigInteger.Parse("1676515877735"), result.AuctionState.Bids[3].Delegator.StakedAmount); + Assert.IsNull(result.AuctionState.Bids[3].Delegator.DelegatorKind.PublicKey); + Assert.AreEqual("8af7b77811970792f98b806779dfc0d1a9fef5bad205c6be8bb884210d7d323c", result.AuctionState.Bids[3].Delegator.DelegatorKind.Purse); } } } \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/get-auction-info-v200.json b/Casper.Network.SDK.Test/TestData/get-auction-info-v200.json index 5c3ce33..730767f 100644 --- a/Casper.Network.SDK.Test/TestData/get-auction-info-v200.json +++ b/Casper.Network.SDK.Test/TestData/get-auction-info-v200.json @@ -1,401 +1,134 @@ { "api_version": "2.0.0", "auction_state": { - "state_root_hash": "1e8f22fb799932c56ffcf4d48c014e09ba9b791a3280f9a8cc9c7614ce7d562e", - "block_height": 1394, + "state_root_hash": "c8228d6d6d45151766901ba3579461847a17db15a66ce9ef6ae2f3e3abffd132", + "block_height": 3480973, "era_validators": [ { - "era_id": 126, + "era_id": 14910, "validator_weights": [ { - "public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", - "weight": "773530002936061175" + "public_key": "01032146b0b9de01e26aaec7b0d1769920de94681dbd432c3530bfe591752ded6c", + "weight": "2030345838471710063" }, { - "public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", - "weight": "2576240053903858104" + "public_key": "0126d4637eb0c0769274f03a696df1112383fa621c9f73f57af4c5c0fbadafa8cf", + "weight": "2030227732934792089" }, { - "public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", - "weight": "1391364929868444236" + "public_key": "0140afe8f752e5ff100e0189c080bc207e8805b3e5e82f792ec608de2f11f39f6c", + "weight": "2030338910449323803" }, { - "public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", - "weight": "743423726792740916" - }, - { - "public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", - "weight": "1345521286498896079" + "public_key": "017536433a73f7562526f3e9fcb8d720428ae2d28788a9909f3c6f637a9d848a4b", + "weight": "2030266847164599460" } ] }, { - "era_id": 127, + "era_id": 14911, "validator_weights": [ { - "public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", - "weight": "776022459374023984" - }, - { - "public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", - "weight": "2596547825066264181" + "public_key": "01032146b0b9de01e26aaec7b0d1769920de94681dbd432c3530bfe591752ded6c", + "weight": "2030438564141544419" }, { - "public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", - "weight": "1401840263593846706" + "public_key": "0126d4637eb0c0769274f03a696df1112383fa621c9f73f57af4c5c0fbadafa8cf", + "weight": "2030321963971741685" }, { - "public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", - "weight": "751821266092616465" + "public_key": "0140afe8f752e5ff100e0189c080bc207e8805b3e5e82f792ec608de2f11f39f6c", + "weight": "2030431133219240217" }, { - "public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", - "weight": "1358848185873249187" + "public_key": "017536433a73f7562526f3e9fcb8d720428ae2d28788a9909f3c6f637a9d848a4b", + "weight": "2030357058487126289" } ] }, { - "era_id": 128, + "era_id": 14912, "validator_weights": [ { - "public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", - "weight": "778514256836908775" - }, - { - "public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", - "weight": "2622847032216478396" + "public_key": "01032146b0b9de01e26aaec7b0d1769920de94681dbd432c3530bfe591752ded6c", + "weight": "2030529783588751162" }, { - "public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", - "weight": "1412321239623424126" + "public_key": "0126d4637eb0c0769274f03a696df1112383fa621c9f73f57af4c5c0fbadafa8cf", + "weight": "2030418207085001407" }, { - "public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", - "weight": "760216038315816781" + "public_key": "0140afe8f752e5ff100e0189c080bc207e8805b3e5e82f792ec608de2f11f39f6c", + "weight": "2030524865448355568" }, { - "public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", - "weight": "1366181433007372460" + "public_key": "017536433a73f7562526f3e9fcb8d720428ae2d28788a9909f3c6f637a9d848a4b", + "weight": "2030445261010189498" } ] } ], "bids": [ { - "public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", - "bid": { - "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", - "bonding_purse": "uref-7141e0057da2902a50a7a0794f8941321e3f32c477f67621239e7964151e8734-007", - "staked_amount": "406303529637252506", - "delegation_rate": 1, - "vesting_schedule": { - "initial_release_timestamp_millis": 1719301233872, - "locked_amounts": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ] - }, - "delegators": [ - { - "delegator_public_key": "018b46617b2b97e633b36530f2964b3f4c15916235910a2737e83d4fa2c7fad542", - "delegator": { - "delegator_public_key": "018b46617b2b97e633b36530f2964b3f4c15916235910a2737e83d4fa2c7fad542", - "staked_amount": "372210727199656269", - "bonding_purse": "uref-841b81b467c6bc114096446d0e121273645f21d91e1e33c0de3fc9398b294aac-007", - "validator_public_key": "01509254f22690fbe7fb6134be574c4fbdb060dfa699964653b99753485e518ea6", - "vesting_schedule": { - "initial_release_timestamp_millis": 1719301233872, - "locked_amounts": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ] - } - } - } - ], - "inactive": false - } - }, - { - "public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", + "public_key": "01358a7e107668ae2eb092dcfbeb97d2ec3cc8354d2a77bc8f232fff6630a826c3", "bid": { - "validator_public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", - "bonding_purse": "uref-d551374a39e56fb24bd5a3b3bf87a1ef7d4c08c6ce7892dcbb4a158ead9822c4-007", - "staked_amount": "1370640008489104648", - "delegation_rate": 1, - "vesting_schedule": { - "initial_release_timestamp_millis": 1719301233872, - "locked_amounts": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ] - }, - "delegators": [ - { - "delegator_public_key": "0197b79d1a1351f8fb922b9f7f556d2bbfdba5105df9eaa6caa07804c703a641ed", - "delegator": { - "delegator_public_key": "0197b79d1a1351f8fb922b9f7f556d2bbfdba5105df9eaa6caa07804c703a641ed", - "staked_amount": "1252207023727373748", - "bonding_purse": "uref-cf1c24cc7b54a82ef379dcc87a3958ef0ffadf817a605b1e425d3a659946350b-007", - "validator_public_key": "0190664e16a17594ed2d0e3c279c4cf5894e8db0da15e3b91c938562a1caae32ab", - "vesting_schedule": { - "initial_release_timestamp_millis": 1719301233872, - "locked_amounts": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ] - } - } - } - ], - "inactive": false + "Validator": { + "validator_public_key": "01358a7e107668ae2eb092dcfbeb97d2ec3cc8354d2a77bc8f232fff6630a826c3", + "bonding_purse": "uref-027d909fa0818f8b426b905795f608a6301168476b8013d7b3f682786796096f-007", + "staked_amount": "2500000000", + "delegation_rate": 1, + "vesting_schedule": null, + "inactive": true, + "minimum_delegation_amount": 500000000000, + "maximum_delegation_amount": 1000000000000000000, + "reserved_slots": 3 + } } }, { - "public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", + "public_key": "020234b6ebc6d9c826964a692951e4e7e0c1e7db2683ebbd4b9b209ad6ca5224f02c", "bid": { - "validator_public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", - "bonding_purse": "uref-5af966267c401835bc3535c8517353eeed45c4e33cac29b50a4bb21d5a137305-007", - "staked_amount": "737000667377542259", - "delegation_rate": 1, - "vesting_schedule": { - "initial_release_timestamp_millis": 1719301233872, - "locked_amounts": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ] - }, - "delegators": [ - { - "delegator_public_key": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", - "delegator": { - "delegator_public_key": "01a5a5b7328118681638be3e06c8749609280dba4c9daf9aeb3d3464b8839b018a", - "staked_amount": "675320572245881867", - "bonding_purse": "uref-b22d651969ee70cd17d188f84dbdb6e12e0d8781c6f6615887ddd2ca4164dd7f-007", - "validator_public_key": "01c867ff3cf1d4e4e68fc00922fdcb740304def196e223091dee62012f444b9eba", - "vesting_schedule": { - "initial_release_timestamp_millis": 1719301233872, - "locked_amounts": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ] - } - } - } - ], - "inactive": false + "Validator": { + "validator_public_key": "020234b6ebc6d9c826964a692951e4e7e0c1e7db2683ebbd4b9b209ad6ca5224f02c", + "bonding_purse": "uref-4a15ba727136c9cb324af202412f3a2cb0d53cd58913647c6e34336f80758f54-007", + "staked_amount": "1000000000", + "delegation_rate": 1, + "vesting_schedule": null, + "inactive": true, + "minimum_delegation_amount": 500000000000, + "maximum_delegation_amount": 1000000000000000000, + "reserved_slots": 0 + } } }, { - "public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", + "public_key": "01032146b0b9de01e26aaec7b0d1769920de94681dbd432c3530bfe591752ded6c", "bid": { - "validator_public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", - "bonding_purse": "uref-ae3f94140d484ffdb28cf447e05edc64df13ca29adc33acca948fa73226b951b-007", - "staked_amount": "395603338820466037", - "delegation_rate": 1, - "vesting_schedule": { - "initial_release_timestamp_millis": 1719301233872, - "locked_amounts": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ] - }, - "delegators": [ - { - "delegator_public_key": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", - "delegator": { - "delegator_public_key": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", - "staked_amount": "364612699495350744", - "bonding_purse": "uref-3ca8c2afabe6120c722f260f53b3dd406178b5341760618ed98f826d9d9aac3d-007", - "validator_public_key": "01f58b94526d280881f79744effebc555426190950d5dfdd2f8aaf10ceaec010c6", - "vesting_schedule": { - "initial_release_timestamp_millis": 1719301233872, - "locked_amounts": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ] - } - } - } - ], - "inactive": false + "Delegator": { + "delegator_kind": { + "Purse": "1cd4fc41e3f750e52024f54b1ac3a757027baad64d47ffb6d4e968967618740b" + }, + "staked_amount": "6435861748906", + "bonding_purse": "uref-30c3f9163e14b9b00c967daaecdba61d9a7a25f454b9f23d556200e1ab847955-007", + "validator_public_key": "01032146b0b9de01e26aaec7b0d1769920de94681dbd432c3530bfe591752ded6c", + "vesting_schedule": null + } } }, { - "public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", + "public_key": "01032146b0b9de01e26aaec7b0d1769920de94681dbd432c3530bfe591752ded6c", "bid": { - "validator_public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", - "bonding_purse": "uref-5c654b8a6f9d0130770c37e6dddca477d02424d718aed53d0315f163c2591180-007", - "staked_amount": "712016868136394248", - "delegation_rate": 1, - "vesting_schedule": { - "initial_release_timestamp_millis": 1719301233872, - "locked_amounts": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ] - }, - "delegators": [ - { - "delegator_public_key": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", - "delegator": { - "delegator_public_key": "0106ed45915392c02b37136618372ac8dde8e0e3b8ee6190b2ca6db539b354ede4", - "staked_amount": "49664621804232", - "bonding_purse": "uref-9120257acebbf71672c3397a08b18374dd995e7e176c7faf9fd0646c2d5a0cdf-007", - "validator_public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", - "vesting_schedule": null - } + "Delegator": { + "delegator_kind": { + "Purse": "8af7b77811970792f98b806779dfc0d1a9fef5bad205c6be8bb884210d7d323c" }, - { - "delegator_public_key": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55", - "delegator": { - "delegator_public_key": "0184f6d260f4ee6869ddb36affe15456de6ae045278fa2f467bb677561ce0dad55", - "staked_amount": "654063155243124749", - "bonding_purse": "uref-64d20811fbd63205cb2267c01b2531198fbeeec69cf1c2611664553cc44bb621-007", - "validator_public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", - "vesting_schedule": { - "initial_release_timestamp_millis": 1719301233872, - "locked_amounts": [ - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0" - ] - } - } - }, - { - "delegator_public_key": "018b46617b2b97e633b36530f2964b3f4c15916235910a2737e83d4fa2c7fad542", - "delegator": { - "delegator_public_key": "018b46617b2b97e633b36530f2964b3f4c15916235910a2737e83d4fa2c7fad542", - "staked_amount": "51745006049231", - "bonding_purse": "uref-a5e8d8ad4ad983dd01270dcf1c7d3d2112ab48b55e2a0347fbbb51066fd686fc-007", - "validator_public_key": "01fed662dc7f1f7af43ad785ba07a8cc05b7a96f9ee69613cfde43bc56bec1140b", - "vesting_schedule": null - } - } - ], - "inactive": false + "staked_amount": "1676515877735", + "bonding_purse": "uref-d34ee21f5fe61feee2d9f15e0e369367aba62ba1b689e59c1e6ddc581ca99fdf-007", + "validator_public_key": "01032146b0b9de01e26aaec7b0d1769920de94681dbd432c3530bfe591752ded6c", + "vesting_schedule": null + } } } ] } -} +} \ No newline at end of file diff --git a/Casper.Network.SDK/Converters/BidsListConverter.cs b/Casper.Network.SDK/Converters/BidsListConverter.cs index 03c981f..eaf560a 100644 --- a/Casper.Network.SDK/Converters/BidsListConverter.cs +++ b/Casper.Network.SDK/Converters/BidsListConverter.cs @@ -6,15 +6,15 @@ namespace Casper.Network.SDK.Converters { - public class BidsListConverter : JsonConverter> + public class BidKindsListConverter : JsonConverter> { - public override List Read( + public override List Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - var bids = new List(); - + var bids = new List(); + if (reader.TokenType != JsonTokenType.StartArray) throw new JsonException("StartArray token expected to deserialize a list of Bids"); @@ -23,10 +23,10 @@ public override List Read( while (reader.TokenType != JsonTokenType.EndArray) { reader.Read(); - + string publicKey = null; - Bid bid = null; - + BidKind bid = null; + while (reader.TokenType == JsonTokenType.PropertyName) { var property = reader.GetString(); @@ -38,24 +38,51 @@ public override List Read( reader.Read(); break; case "bid": - bid = JsonSerializer.Deserialize(ref reader, options); - reader.Read(); // end object + try + { + using (JsonDocument document = JsonDocument.ParseValue(ref reader)) + { + if (document.RootElement.TryGetProperty("bonding_purse", out var bondingPurse)) + { + var unifiedBid = + JsonSerializer.Deserialize(document.RootElement.GetRawText()); + bid = new BidKind() + { + Unified = unifiedBid, + }; + } + else + { + bid = JsonSerializer.Deserialize(document.RootElement.GetRawText()); + } + + reader.Read(); // read end of object + } + } + catch (Exception e) + { + throw new JsonException(e.Message); + } + break; default: throw new JsonException($"Unexpected property '{property}' deserializing Bid"); } } - if (bid != null && bid.PublicKey == null && publicKey != null) - bid = new Bid + if (publicKey != null && bid != null && bid.Unified != null && bid.Unified.PublicKey == null) + bid = new BidKind() { - BondingPurse = bid.BondingPurse, - DelegationRate = bid.DelegationRate, - Delegators = bid.Delegators, - Inactive = bid.Inactive, - StakedAmount = bid.StakedAmount, - PublicKey = PublicKey.FromHexString(publicKey), - VestingSchedule = bid.VestingSchedule, + Unified = new Bid() + { + BondingPurse = bid.Unified.BondingPurse, + DelegationRate = bid.Unified.DelegationRate, + Delegators = bid.Unified.Delegators, + Inactive = bid.Unified.Inactive, + StakedAmount = bid.Unified.StakedAmount, + PublicKey = PublicKey.FromHexString(publicKey), + VestingSchedule = bid.Unified.VestingSchedule, + } }; bids.Add(bid); @@ -69,7 +96,7 @@ public override List Read( public override void Write( Utf8JsonWriter writer, - List value, + List value, JsonSerializerOptions options) { throw new NotImplementedException("Write method for Bid not yet implemented"); diff --git a/Casper.Network.SDK/Converters/NamedArgsListConverter.cs b/Casper.Network.SDK/Converters/NamedArgsListConverter.cs index 570cda5..bfdc0a9 100644 --- a/Casper.Network.SDK/Converters/NamedArgsListConverter.cs +++ b/Casper.Network.SDK/Converters/NamedArgsListConverter.cs @@ -21,16 +21,22 @@ public override List Read( reader.Read(); if (named != "Named") throw new JsonException("Named property expected"); - + reader.Read(); // Start array List list = new List(); - if (reader.TokenType == JsonTokenType.EndObject || - reader.TokenType == JsonTokenType.EndArray) + if (reader.TokenType == JsonTokenType.EndObject) return list; //this is an empty list, just return... - var tConverter = Activator.CreateInstance(typeof(TConverter)) as JsonConverter; + if (reader.TokenType == JsonTokenType.EndArray) + { + //this is an empty list, skip end array item and return... + reader.Read(); + return list; + } + + var tConverter = Activator.CreateInstance(typeof(TConverter)) as JsonConverter; if (reader.TokenType is JsonTokenType.StartArray or JsonTokenType.StartObject or JsonTokenType.PropertyName) { diff --git a/Casper.Network.SDK/JsonRpc/CasperMethods.cs b/Casper.Network.SDK/JsonRpc/CasperMethods.cs index e8cae22..6e7db89 100644 --- a/Casper.Network.SDK/JsonRpc/CasperMethods.cs +++ b/Casper.Network.SDK/JsonRpc/CasperMethods.cs @@ -61,6 +61,25 @@ public GetAuctionInfo(ulong height) : base("state_get_auction_info", height) { } } + + public class GetAuctionInfoV2 : RpcMethod + { + /// + /// Returns the bids and validators at a given block. + /// + /// Block hash for which the auction info is queried. Null for the most recent auction info. + public GetAuctionInfoV2(string blockHash) : base("state_get_auction_info_v2", blockHash) + { + } + + /// + /// Returns the bids and validators at a given block. + /// + /// Block height for which the auction info is queried. + public GetAuctionInfoV2(ulong height) : base("state_get_auction_info_v2", height) + { + } + } public class GetAccountInfo : RpcMethod { diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index bef3827..da48e4a 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -21,6 +21,8 @@ public class NetCasperClient : ICasperClient, IDisposable private readonly RpcClient _rpcClient; + private int _nodeVersion; + /// /// Create a new instance of the Casper client for a specific node in the network determined /// by the node address. Optionally, indicate a logging handler to log the requests and responses @@ -44,6 +46,20 @@ private Task> SendRpcRequestAsync(RpcMethod return _rpcClient.SendRpcRequestAsync(method); } + private async Task GetNodeVersion() + { + if (_nodeVersion > 0) + return _nodeVersion; + + var response = await this.GetNodeStatus(); + var result = response.Parse(); + _nodeVersion = result.BuildVersion.StartsWith("2") + ? 2 + : 1; + + return _nodeVersion; + } + /// /// Request the state root hash at a given Block. /// @@ -93,8 +109,16 @@ public async Task> GetNodePeers() /// public async Task> GetAuctionInfo(string blockHash = null) { - var method = new GetAuctionInfo(blockHash); - return await SendRpcRequestAsync(method); + if (await GetNodeVersion() == 2) + { + var method = new GetAuctionInfoV2(blockHash); + return await SendRpcRequestAsync(method); + } + else + { + var method = new GetAuctionInfo(blockHash); + return await SendRpcRequestAsync(method); + } } /// @@ -103,8 +127,16 @@ public async Task> GetAuctionInfo(string block /// Block height for which the auction info is queried. public async Task> GetAuctionInfo(ulong blockHeight) { - var method = new GetAuctionInfo(blockHeight); - return await SendRpcRequestAsync(method); + if (await GetNodeVersion() == 2) + { + var method = new GetAuctionInfoV2(blockHeight); + return await SendRpcRequestAsync(method); + } + else + { + var method = new GetAuctionInfo(blockHeight); + return await SendRpcRequestAsync(method); + } } /// diff --git a/Casper.Network.SDK/Types/AuctionState.cs b/Casper.Network.SDK/Types/AuctionState.cs index d537dfe..1cd498c 100644 --- a/Casper.Network.SDK/Types/AuctionState.cs +++ b/Casper.Network.SDK/Types/AuctionState.cs @@ -13,9 +13,9 @@ public class AuctionState /// All bids contained within a vector. /// [JsonPropertyName("bids")] - [JsonConverter(typeof(BidsListConverter))] - public List Bids { get; init; } - + [JsonConverter(typeof(BidKindsListConverter))] + public List Bids { get; init; } + /// /// Block height. /// From 69fccaf62fd10cb87fccacf5a78f93c2b7cab3a8 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 5 Feb 2025 10:27:41 +0100 Subject: [PATCH 113/126] Convert Unified BidKind to Validator&Delegator bid kinds. Use correct rpc method depending on node and block versions. Signed-off-by: David Hernando --- .../Converters/BidsListConverter.cs | 41 +++++++++++++++---- Casper.Network.SDK/NetCasperClient.cs | 34 +++++++++++---- 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/Casper.Network.SDK/Converters/BidsListConverter.cs b/Casper.Network.SDK/Converters/BidsListConverter.cs index eaf560a..9072740 100644 --- a/Casper.Network.SDK/Converters/BidsListConverter.cs +++ b/Casper.Network.SDK/Converters/BidsListConverter.cs @@ -8,6 +8,9 @@ namespace Casper.Network.SDK.Converters { public class BidKindsListConverter : JsonConverter> { + const ulong DefaultMinimumDelegationAmount = 500l * 1_000_000_000l; + const ulong DefaultMaximumDelegationAmount = 1_000_000_000 * 1_000_000_000l; + public override List Read( ref Utf8JsonReader reader, Type typeToConvert, @@ -70,21 +73,41 @@ public override List Read( } } - if (publicKey != null && bid != null && bid.Unified != null && bid.Unified.PublicKey == null) - bid = new BidKind() + if (bid?.Unified != null) + { + // Convert Unified bids to 1 Validator BidKind and 1 Delegator BidKind per delegator included + bids.Add(new BidKind() { - Unified = new Bid() + Validator = new ValidatorBid() { + PublicKey = bid.Unified.PublicKey ?? PublicKey.FromHexString(publicKey), BondingPurse = bid.Unified.BondingPurse, DelegationRate = bid.Unified.DelegationRate, - Delegators = bid.Unified.Delegators, - Inactive = bid.Unified.Inactive, StakedAmount = bid.Unified.StakedAmount, - PublicKey = PublicKey.FromHexString(publicKey), - VestingSchedule = bid.Unified.VestingSchedule, + MinimumDelegationAmount = DefaultMinimumDelegationAmount, + MaximumDelegationAmount = DefaultMaximumDelegationAmount, + ReservedSlots = 0, + Inactive = bid.Unified.Inactive, } - }; - bids.Add(bid); + }); + + foreach (var delegator in bid.Unified.Delegators) + { + bids.Add(new BidKind() + { + Delegator = new DelegatorBid() + { + VestingSchedule = delegator.VestingSchedule, + ValidatorPublicKey = delegator.ValidatorPublicKey, + StakedAmount = delegator.StakedAmount, + BondingPurse = delegator.BondingPurse, + DelegatorKind = new DelegatorKind() { PublicKey = delegator.DelegatorPublicKey }, + } + }); + } + } + else + bids.Add(bid); if (reader.TokenType != JsonTokenType.EndObject) throw new JsonException("End object token expected while deserializing a list of Bids"); diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index da48e4a..2e7ea01 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -109,16 +109,23 @@ public async Task> GetNodePeers() /// public async Task> GetAuctionInfo(string blockHash = null) { - if (await GetNodeVersion() == 2) - { - var method = new GetAuctionInfoV2(blockHash); - return await SendRpcRequestAsync(method); - } - else + var nodeVersion = await GetNodeVersion(); + RpcMethod method = null; + + if (nodeVersion == 1) + method = new GetAuctionInfo(blockHash); + else if (blockHash == null) + method = new GetAuctionInfoV2(blockHash); + else { - var method = new GetAuctionInfo(blockHash); - return await SendRpcRequestAsync(method); + var getBlockResponse = await GetBlock(blockHash); + if (getBlockResponse.Parse().Block.Version == 2) + method = new GetAuctionInfoV2(blockHash); + else + method = new GetAuctionInfo(blockHash); } + + return await SendRpcRequestAsync(method); } /// @@ -954,6 +961,17 @@ public static async Task GetNodeMetrics(string host, int port) return await GetNodeMetrics(uriBuilder.Uri.ToString()); } + /// + /// Returns the validator bid. + /// + /// The public key of the validator. + /// Hash of the block to retrieve the rewards from. Null for the most recent era + public async Task> GetValidatorBid(PublicKey validator, string blockHash = null) + { + var bidAddr = BidAddrKey.FromValidatorKey(new AccountHashKey(validator)); + return await QueryGlobalState(bidAddr, blockHash); + } + public void Dispose() { Dispose(true); From 165b418d12cb00b7dbc9d9f5e6e7cff283220999 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 5 Feb 2025 10:31:04 +0100 Subject: [PATCH 114/126] Repeat for get by height. Signed-off-by: David Hernando --- Casper.Network.SDK/NetCasperClient.cs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index 2e7ea01..ee9006b 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -134,16 +134,21 @@ public async Task> GetAuctionInfo(string block /// Block height for which the auction info is queried. public async Task> GetAuctionInfo(ulong blockHeight) { - if (await GetNodeVersion() == 2) - { - var method = new GetAuctionInfoV2(blockHeight); - return await SendRpcRequestAsync(method); - } - else + var nodeVersion = await GetNodeVersion(); + RpcMethod method = null; + + if (nodeVersion == 1) + method = new GetAuctionInfo(blockHeight); + else { - var method = new GetAuctionInfo(blockHeight); - return await SendRpcRequestAsync(method); + var getBlockResponse = await GetBlock(blockHeight); + if (getBlockResponse.Parse().Block.Version == 2) + method = new GetAuctionInfoV2(blockHeight); + else + method = new GetAuctionInfo(blockHeight); } + + return await SendRpcRequestAsync(method); } /// From 587be7a136243b2b3df72e75783b3410a49072d6 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 5 Feb 2025 19:19:29 +0100 Subject: [PATCH 115/126] missed file in last commit Signed-off-by: David Hernando --- .../RPCResponses/GetAuctionInfoResultTest.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs index cf88bb6..a47107d 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs @@ -27,12 +27,15 @@ public void GetBlockResultTest_v156() Assert.AreEqual("020377bc3ad54b5505971e001044ea822a3f6f307f8dc93fa45a05b7463c0a053bed", result.AuctionState.EraValidators[0].ValidatorWeights[4].PublicKey.ToString().ToLower()); Assert.AreEqual(BigInteger.Parse("10567495110201092"), result.AuctionState.EraValidators[0].ValidatorWeights[4].Weight); - Assert.AreEqual(3, result.AuctionState.Bids.Count); - Assert.AreEqual("01001b79b9a6e13d2b96e916f7fa7dff40496ba5188479263ca0fb2ccf8b714305", result.AuctionState.Bids[0].Unified.PublicKey.ToString().ToLower()); - Assert.AreEqual(1, result.AuctionState.Bids[0].Unified.Delegators.Count); - Assert.AreEqual("018b34b15e023844531621cb52d42e216a2ea56034f0f40bf7cee566c32eae4f83", result.AuctionState.Bids[0].Unified.Delegators[0].DelegatorPublicKey.ToString().ToLower()); - Assert.AreEqual(BigInteger.Parse("30268476029"), result.AuctionState.Bids[0].Unified.Delegators[0].StakedAmount); - Assert.IsNull(result.AuctionState.Bids[0].Unified.Delegators[0].VestingSchedule); + Assert.AreEqual(6, result.AuctionState.Bids.Count); + Assert.AreEqual(3, result.AuctionState.Bids.Count(b => b.Validator != null)); + Assert.AreEqual(3, result.AuctionState.Bids.Count(b => b.Delegator != null)); + Assert.AreEqual("01001b79b9a6e13d2b96e916f7fa7dff40496ba5188479263ca0fb2ccf8b714305", result.AuctionState.Bids[4].Validator.PublicKey.ToString().ToLower()); + Assert.AreEqual(BigInteger.Parse("908982507030"), result.AuctionState.Bids[4].Validator.StakedAmount); + Assert.AreEqual("01001b79b9a6e13d2b96e916f7fa7dff40496ba5188479263ca0fb2ccf8b714305", result.AuctionState.Bids[5].Delegator.ValidatorPublicKey.ToString().ToLower()); + Assert.AreEqual("018b34b15e023844531621cb52d42e216a2ea56034f0f40bf7cee566c32eae4f83", result.AuctionState.Bids[5].Delegator.DelegatorKind.PublicKey.ToString().ToLower()); + Assert.AreEqual(BigInteger.Parse("30268476029"), result.AuctionState.Bids[5].Delegator.StakedAmount); + Assert.AreEqual("uref-401f87167d733d8dd7d3efbf135a91ccd42fffb77b02d4a0075b963f14f1fbb4-007", result.AuctionState.Bids[5].Delegator.BondingPurse.ToString()); } [Test] From bf866effaff9aeb65d1dd8f774138bae3c53568f Mon Sep 17 00:00:00 2001 From: David Hernando Date: Wed, 5 Feb 2025 19:20:32 +0100 Subject: [PATCH 116/126] Fixed disabled_versions deserialization Signed-off-by: David Hernando --- .../RPCResponses/GetPackageResultTest.cs | 4 +- ...-package-result-contract-package-v200.json | 9 +- Casper.Network.SDK/Types/ContractPackage.cs | 84 ++++++++++++++++--- 3 files changed, 83 insertions(+), 14 deletions(-) diff --git a/Casper.Network.SDK.Test/RPCResponses/GetPackageResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetPackageResultTest.cs index 2833370..b42a2a2 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetPackageResultTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetPackageResultTest.cs @@ -19,9 +19,11 @@ public void GetPackageResultContractPackageTest_v200() Assert.IsNotNull(result.ContractPackage); Assert.IsNull(result.Package); Assert.AreEqual("uref-6fc684fea74b278cbb18b546a6d9242b810ce58a2ff05d17493b19aa08f540e0-007", result.ContractPackage.AccessKey.ToString()); - Assert.AreEqual(1, result.ContractPackage.Versions.Count); + Assert.AreEqual(2, result.ContractPackage.Versions.Count); Assert.AreEqual(2, result.ContractPackage.Versions[0].ProtocolVersionMajor); Assert.AreEqual(1, result.ContractPackage.Versions[0].Version); + Assert.AreEqual(2, result.ContractPackage.DisabledVersions[0].ProtocolVersionMajor); + Assert.AreEqual(4, result.ContractPackage.DisabledVersions[0].Version); Assert.AreEqual("contract-25aa2d3cc62a302746c08ae885454d6e8a9c8609aaa7468b24284e5d29c5d2f1", result.ContractPackage.Versions[0].Hash); Assert.AreEqual(LockStatus.Unlocked, result.ContractPackage.LockStatus); } diff --git a/Casper.Network.SDK.Test/TestData/get-package-result-contract-package-v200.json b/Casper.Network.SDK.Test/TestData/get-package-result-contract-package-v200.json index 2a62f13..683e54b 100644 --- a/Casper.Network.SDK.Test/TestData/get-package-result-contract-package-v200.json +++ b/Casper.Network.SDK.Test/TestData/get-package-result-contract-package-v200.json @@ -8,9 +8,16 @@ "protocol_version_major": 2, "contract_version": 1, "contract_hash": "contract-25aa2d3cc62a302746c08ae885454d6e8a9c8609aaa7468b24284e5d29c5d2f1" + }, + { + "protocol_version_major": 2, + "contract_version": 4, + "contract_hash": "contract-2222222222222222222222222222222222222222222222222222222222222222" } ], - "disabled_versions": [], + "disabled_versions": [ + [2,4] + ], "groups": [], "lock_status": "Unlocked" } diff --git a/Casper.Network.SDK/Types/ContractPackage.cs b/Casper.Network.SDK/Types/ContractPackage.cs index 0881aba..5c1575b 100644 --- a/Casper.Network.SDK/Types/ContractPackage.cs +++ b/Casper.Network.SDK/Types/ContractPackage.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using System.Text.Json; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; @@ -7,6 +9,7 @@ namespace Casper.Network.SDK.Types /// /// A disabled version of a contract. /// + [JsonConverter(typeof(DisabledVersion.DisabledVersionConverter))] public class DisabledVersion { /// @@ -14,11 +17,68 @@ public class DisabledVersion /// [JsonPropertyName("contract_version")] public uint Version { get; init; } - + [JsonPropertyName("protocol_version_major")] public uint ProtocolVersionMajor { get; init; } + + public class DisabledVersionConverter : JsonConverter + { + public override DisabledVersion Read + ( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options + ) + { + if (reader.TokenType == JsonTokenType.StartObject) + { + using (JsonDocument doc = JsonDocument.ParseValue(ref reader)) + { + doc.RootElement.TryGetProperty("protocol_version_major", out var protocolVersionMajor); + doc.RootElement.TryGetProperty("contract_version", out var contractVersion); + if (!protocolVersionMajor.ValueKind.Equals(JsonValueKind.Number) || + !contractVersion.ValueKind.Equals(JsonValueKind.Number)) + throw new JsonException("Cannot deserialize StoredValue. Number expected for protocol_version_major and contract_version."); + return new DisabledVersion() + { + ProtocolVersionMajor = protocolVersionMajor.GetUInt32(), + Version = contractVersion.GetUInt32() + }; + } + } + + if (reader.TokenType != JsonTokenType.StartArray) + throw new JsonException("Cannot deserialize StoredValue. StartObject expected."); + { + using (JsonDocument doc = JsonDocument.ParseValue(ref reader)) + { + var versionsArray = JsonSerializer.Deserialize>(doc.RootElement.GetRawText()); + + if (versionsArray?.Count != 2) + throw new JsonException( + "Cannot deserialize StoredValue. Array of length 2 expected for disabled_version item."); + + return new DisabledVersion() + { + ProtocolVersionMajor = versionsArray[0], + Version = versionsArray[1] + }; + } + } + + } + + + public override void Write( + Utf8JsonWriter writer, + DisabledVersion value, + JsonSerializerOptions options) + { + throw new NotImplementedException("Write method for DisabledVersion not yet implemented"); + } + } } - + /// /// Information related to an active version of a contract. /// @@ -29,13 +89,13 @@ public class ContractVersion /// [JsonPropertyName("contract_hash")] public string Hash { get; init; } - + /// /// Contract version. /// [JsonPropertyName("contract_version")] public uint Version { get; init; } - + [JsonPropertyName("protocol_version_major")] public uint ProtocolVersionMajor { get; init; } } @@ -50,7 +110,7 @@ public class Group /// [JsonPropertyName("group")] public string Label { get; init; } - + /// /// List of URefs associated with the group label. /// @@ -67,7 +127,7 @@ public enum LockStatus Locked, Unlocked, } - + /// /// Contract definition, metadata, and security container. /// @@ -76,13 +136,13 @@ public class ContractPackage [JsonPropertyName("access_key")] [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] public URef AccessKey { get; init; } - + /// /// List of disabled versions of a contract. /// [JsonPropertyName("disabled_versions")] public List DisabledVersions { get; init; } - + /// /// Groups associate a set of URefs with a label. Entry points on a contract can be given /// a list of labels they accept and the runtime will check that a URef from at least one @@ -90,18 +150,18 @@ public class ContractPackage /// [JsonPropertyName("groups")] public List Groups { get; init; } - + /// /// List of active versions of a contract. /// [JsonPropertyName("versions")] public List Versions { get; init; } - + /// /// The current state of the contract package. /// - [JsonPropertyName("lock_status")] + [JsonPropertyName("lock_status")] [JsonConverter(typeof(JsonStringEnumConverter))] public LockStatus LockStatus { get; init; } } -} +} \ No newline at end of file From 4b94dd85b90bd9cfc677789ef6fc055c818849d1 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 4 Mar 2025 16:16:39 +0100 Subject: [PATCH 117/126] Updates to migration guide Signed-off-by: David Hernando --- Docs/Articles/Casper20MigrationGuide.md | 31 ++++++------------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/Docs/Articles/Casper20MigrationGuide.md b/Docs/Articles/Casper20MigrationGuide.md index c07b754..a9e152c 100644 --- a/Docs/Articles/Casper20MigrationGuide.md +++ b/Docs/Articles/Casper20MigrationGuide.md @@ -64,7 +64,7 @@ if (block.Version == 1) { ### Transactions included in a block -Blocks produced on Casper v1.x contain a list of deploys that differentiate between native transfers and all other types of deploys. On Casper 2.0, transactions (this includes legacy deploys too) have a category field that determines the processing lane to which the transaction is sent. The chainspec of the network determines the properties of each lane: +Blocks produced on Casper v1.x contain a list of deploys that differentiate between native transfers and all other types of deploys. On Casper 2.0, transactions (this includes legacy deploys too) the payment amount determines the processing lane to which the transaction is sent. The chainspec of the network determines the properties of each lane: 1. Maximum transaction size in bytes for a given transaction in a certain lane. 2. Maximum args length size in bytes for a given transaction in a certain lane. @@ -113,30 +113,13 @@ foreach(var transfer in result.Transfers) } ``` -## Balances - -The new `NoFee` mode in Casper 2.0 introduces the concept of a ‘balance hold’. In this mode of operation, for each transaction, the network holds the amount paid for its execution in the paying purse. Thus, entities have a total balance and an available balance, where the available balance is equal to the total balance minus the held balance. - -To get detailed information on an entity balance, use the new method `QueryBalanceDetails(IPurseIdentifier)` with a purse identifier. `PublicKey`, `AccountHashKey`, `AddressableEntityKey`, and `URef` keys implement the `IPurseIdentifier` interface and can be used to retrieve the balance details for an entity. - -`GetAccountBalance` has been renamed to `GetBalance` since this method can be used for any type of entity, not only for accounts. - -## Deploys and Transactions - -Condor introduces a new transaction model to support advanced use cases. While `Deploy`s continue to work in Condor, this type is deprecated, and Casper recommends switching to the new `TransactionV1` type. - -Similar to the `DeployTemplates` class, which provides deploy templates for most common use cases, we've implemented a `TransactionBuilder` class to facilitate the creation of transactions using the new `TransactionV1` model for different use cases. Check the documentation and the examples for more information. - -Use the new method `PutTransaction` to send a `TransactionV1` to the network. Use the new `GetTransaction` method to retrieve an accepted transaction. Both methods can also be used to send and retrieve a `Deploy`. For unprocessed transactions, the ExecutionInfo in the response is `null`. Upon processing, this property contains all information about the execution, including cost, payments, errors (if any), and execution effects. - - ### GetDeploy() Response from the `GetDeploy()` method has changed. Instead of a list of `ExecutionResult` objects, it now returns an instance of `ExecutionInfo` for a processed deploy. This instance contains block information and a results object. ### Payments and costs -For a transaction (old and new versions) processed in Condor, the execution results object contains three properties related to the gas consumed and the CSPR tokens paid: +For a transaction (old and new versions) processed in Casper 2.0, the execution results object contains three properties related to the gas consumed and the CSPR tokens paid: - `limit`: The maximum allowed gas limit for the transaction. - `consumed`: Gas consumed executing the transaction. @@ -146,7 +129,7 @@ In the NoFee model, the user does not specify any amount for payment. Instead, h ### Execution results -The `ExecutionResult` class is a versioned object. Deploys processed before the Condor upgrade are stored in the global state as `ExecutionResultV1`records, and deploys and transactions processed after the Condor upgrade are stored as `ExecutionResultV2` records. +The `ExecutionResult` class is a versioned object. Deploys processed before the Casper 2.0 upgrade are stored in the global state as `ExecutionResultV1`records, and deploys and transactions processed after the Casper 2.0 upgrade are stored as `ExecutionResultV2` records. For a processed transaction, the `GetTransaction` method always returns an `ExecutionResult` instance regardless of the version. It has the same fields as `ExecutionResultV2`. You can obtain the original structure by casting this instance to the correct version: @@ -160,11 +143,11 @@ if (executionResult.Version == 2) { } ``` -The Effect property contains a list of Transforms that modify the global state due to the transaction execution. Note that the ExecutionEffect type in Casper v1.x, which also contained a list of operations in addition to the transforms, has been removed in Condor execution results. +The Effect property contains a list of Transforms that modify the global state due to the transaction execution. Note that the ExecutionEffect type in Casper v1.x, which also contained a list of operations in addition to the transforms, has been removed in Casper 2.0 execution results. ## Auction contract -The auction contract has also changed in Condor. If your application tracks validator bids, rewards, and delegators, you must rework how network responses are parsed and interpreted. A complete description of the changes cannot be covered in this guide. +The auction contract has also changed in Casper 2.0. If your application tracks validator bids, rewards, and delegators, you must rework how network responses are parsed and interpreted. A complete description of the changes cannot be covered in this guide. ## Server Sent Events @@ -176,7 +159,7 @@ The BlockedAdded event contains a Block object. The original block structure, Bl ### Transactions -On Condor, the `DeployAccepted`, `DeployProcessed`, and `DeployExpired` events are replaced with the equivalent `TransactionAccepted`, `TransactionProcessed`, and `TransactionExpired` events. These are emitted for both `Deploy` and `TransactionV1` types of transactions. +On Casper 2.0, the `DeployAccepted`, `DeployProcessed`, and `DeployExpired` events are replaced with the equivalent `TransactionAccepted`, `TransactionProcessed`, and `TransactionExpired` events. These are emitted for both `Deploy` and `TransactionV1` types of transactions. A `TransactionAccepted` event contains a `Transaction` property with either a `Deploy` or a `TransactionV1`. `TransactionProcessed` and `TransactionExpired` events contain a `TransactionHash` property with either a deploy hash or a transaction version 1 hash. @@ -200,7 +183,7 @@ if (finalitySignature.Version == 2) { ### Last switch block hash -For a Condor network, the `GetNodeStatus` method can be used to get the latest switch block hash. The response contains the `LatestSwitchBlockHash` property with this value. +For a Casper 2.0 network, the `GetNodeStatus` method can be used to get the latest switch block hash. The response contains the `LatestSwitchBlockHash` property with this value. Also, for blocks produced in Casper 2.0, the `Block` instance contains the previous switch block hash in the `LastSwitchBlockHash` property. From c11a784e9609fda43ddd13c10ba632dfb4f04d48 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Tue, 4 Mar 2025 16:21:54 +0100 Subject: [PATCH 118/126] Added WriteContractTransform_v200 test Signed-off-by: David Hernando --- .../TestData/write_contract_transform_v2.json | 452 ++++++++++++++++++ Casper.Network.SDK.Test/TransformsTest.cs | 27 ++ .../Types/GlobalStateKey/BidAddrKey.cs | 6 + .../Types/TransactionBuilder.cs | 6 + 4 files changed, 491 insertions(+) create mode 100644 Casper.Network.SDK.Test/TestData/write_contract_transform_v2.json diff --git a/Casper.Network.SDK.Test/TestData/write_contract_transform_v2.json b/Casper.Network.SDK.Test/TestData/write_contract_transform_v2.json new file mode 100644 index 0000000..3103967 --- /dev/null +++ b/Casper.Network.SDK.Test/TestData/write_contract_transform_v2.json @@ -0,0 +1,452 @@ +{ + "key": "hash-94b21891ae273b17eeb6a1899a52ab952bcc4da2e19626563f88d6cf7ab6a2bd", + "kind": { + "Write": { + "Contract": { + "contract_package_hash": "contract-package-b71675d8cf701d9bc584cb5152706873110e5158b004fb966ed28be49c66b39a", + "contract_wasm_hash": "contract-wasm-a9fb7ec293465829432a8e543a2c2d5bba6d622c512af7e0cf204f6f536a1cbf", + "named_keys": [ + { + "name": "__events", + "key": "uref-821900c35dcc7bedfa01d4901621990552d80870228b4d9a37e47364ae7d12a6-007" + }, + { + "name": "__events_ces_version", + "key": "uref-993d25549280a839ef18c81d1211e86766ff29e7b7685912bc0e4e4a4964d886-007" + }, + { + "name": "__events_length", + "key": "uref-c640355c74023b0eb6025d376285235767ffe73d9be57d83576e647aa720eacc-007" + }, + { + "name": "__events_schema", + "key": "uref-5bf9cb0884874cc7d80e5bcab54c5100bfc8773908b85099ab569b32bd6bd72d-007" + }, + { + "name": "state", + "key": "uref-c5ae802a50fb72194d3c543805bab1a612186bdf2cc5d62758595694a1928fff-007" + } + ], + "entry_points": [ + { + "name": "add_to_the_pool", + "entry_point": { + "name": "add_to_the_pool", + "args": [], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "allowance", + "entry_point": { + "name": "allowance", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "spender", + "cl_type": "Key" + } + ], + "ret": "U256", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "approve", + "entry_point": { + "name": "approve", + "args": [ + { + "name": "spender", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "balance_of", + "entry_point": { + "name": "balance_of", + "args": [ + { + "name": "account", + "cl_type": "Key" + } + ], + "ret": "U256", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "burn", + "entry_point": { + "name": "burn", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "change_security", + "entry_point": { + "name": "change_security", + "args": [ + { + "name": "admin_list", + "cl_type": { + "List": "Key" + } + }, + { + "name": "minter_list", + "cl_type": { + "List": "Key" + } + }, + { + "name": "none_list", + "cl_type": { + "List": "Key" + } + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "claim", + "entry_point": { + "name": "claim", + "args": [ + { + "name": "receipt_id", + "cl_type": "U32" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "decimals", + "entry_point": { + "name": "decimals", + "args": [], + "ret": "U8", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "decrease_allowance", + "entry_point": { + "name": "decrease_allowance", + "args": [ + { + "name": "spender", + "cl_type": "Key" + }, + { + "name": "decr_by", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "grant_role", + "entry_point": { + "name": "grant_role", + "args": [ + { + "name": "role", + "cl_type": { + "ByteArray": 32 + } + }, + { + "name": "address", + "cl_type": "Key" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "has_role", + "entry_point": { + "name": "has_role", + "args": [ + { + "name": "role", + "cl_type": { + "ByteArray": 32 + } + }, + { + "name": "address", + "cl_type": "Key" + } + ], + "ret": "Bool", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "increase_allowance", + "entry_point": { + "name": "increase_allowance", + "args": [ + { + "name": "spender", + "cl_type": "Key" + }, + { + "name": "inc_by", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "init", + "entry_point": { + "name": "init", + "args": [ + { + "name": "validator_address", + "cl_type": "PublicKey" + }, + { + "name": "claim_time", + "cl_type": "U64" + } + ], + "ret": "Unit", + "access": { + "Groups": [ + "constructor_group" + ] + }, + "entry_point_type": "Called" + } + }, + { + "name": "mint", + "entry_point": { + "name": "mint", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "name", + "entry_point": { + "name": "name", + "args": [], + "ret": "String", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "remove_from_the_pool", + "entry_point": { + "name": "remove_from_the_pool", + "args": [ + { + "name": "amount", + "cl_type": "U512" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "revoke_role", + "entry_point": { + "name": "revoke_role", + "args": [ + { + "name": "role", + "cl_type": { + "ByteArray": 32 + } + }, + { + "name": "address", + "cl_type": "Key" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "stake", + "entry_point": { + "name": "stake", + "args": [], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "staked_cspr", + "entry_point": { + "name": "staked_cspr", + "args": [], + "ret": "U512", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "symbol", + "entry_point": { + "name": "symbol", + "args": [], + "ret": "String", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "total_supply", + "entry_point": { + "name": "total_supply", + "args": [], + "ret": "U256", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "transfer", + "entry_point": { + "name": "transfer", + "args": [ + { + "name": "recipient", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "transfer_from", + "entry_point": { + "name": "transfer_from", + "args": [ + { + "name": "owner", + "cl_type": "Key" + }, + { + "name": "recipient", + "cl_type": "Key" + }, + { + "name": "amount", + "cl_type": "U256" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "unstake", + "entry_point": { + "name": "unstake", + "args": [ + { + "name": "scspr_amount", + "cl_type": "U256" + } + ], + "ret": "U32", + "access": "Public", + "entry_point_type": "Called" + } + }, + { + "name": "withdraw_from_the_pool", + "entry_point": { + "name": "withdraw_from_the_pool", + "args": [ + { + "name": "amount", + "cl_type": "U512" + } + ], + "ret": "Unit", + "access": "Public", + "entry_point_type": "Called" + } + } + ], + "protocol_version": "2.0.0" + } + } + } +} diff --git a/Casper.Network.SDK.Test/TransformsTest.cs b/Casper.Network.SDK.Test/TransformsTest.cs index 75aad7b..2526c76 100644 --- a/Casper.Network.SDK.Test/TransformsTest.cs +++ b/Casper.Network.SDK.Test/TransformsTest.cs @@ -1,6 +1,7 @@ using System.IO; using System.Linq; using System.Numerics; +using System.Text.Json; using Casper.Network.SDK.JsonRpc.ResultTypes; using Casper.Network.SDK.Types; using NUnit.Framework; @@ -57,5 +58,31 @@ kind.Value.CLValue is not null && kind.Value.Unbonding is not null)); } + [Test] + public void WriteContractTransform_v200() + { + string json = File.ReadAllText(TestContext.CurrentContext.TestDirectory + + "/TestData/write_contract_transform_v2.json"); + + var options = new JsonSerializerOptions() + { + WriteIndented = false, + Converters = + { + new Transform.TransformConverter(), + new StoredValue.StoredValueConverter(), + } + }; + var transform = JsonSerializer.Deserialize(json, options); + + Assert.IsNotNull(transform); + Assert.IsNotNull(transform.Key); + var writeTransform = transform.Kind as WriteTransformKind; + Assert.IsNotNull(writeTransform); + Assert.IsNotNull(writeTransform.Value); + var contract = writeTransform.Value.Contract; + Assert.IsNotNull(contract); + Assert.IsTrue(contract.EntryPoints.Count > 0); + } } } \ No newline at end of file diff --git a/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs index 700b882..55bd857 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/BidAddrKey.cs @@ -154,5 +154,11 @@ public BidAddrKey(string key) : base(key, KEYPREFIX) public BidAddrKey(byte[] key) : this(KEYPREFIX + Hex.ToHexString(key)) { } + + public static BidAddrKey FromValidatorKey(AccountHashKey accountHashKey) + { + return new BidAddrKey(KEYPREFIX + Hex.ToHexString(new byte[] {(byte)BidAddrTag.Validator}) + + accountHashKey.ToHexString()); + } } } diff --git a/Casper.Network.SDK/Types/TransactionBuilder.cs b/Casper.Network.SDK/Types/TransactionBuilder.cs index 858887a..ac9db51 100644 --- a/Casper.Network.SDK/Types/TransactionBuilder.cs +++ b/Casper.Network.SDK/Types/TransactionBuilder.cs @@ -67,6 +67,12 @@ public T Payment(IPricingMode pricingMode) return (T)this; } + public T Payment(ulong amount, byte gasPriceTolerance = 1) + { + _pricingMode = Types.PricingMode.PaymentLimited(amount, gasPriceTolerance); + return (T)this; + } + protected void ValidateRequiredProperties() { var missingProperties = this.GetType() From 4918edb8d573be0bd2528db4962caae3971a92eb Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 6 Mar 2025 17:10:10 +0100 Subject: [PATCH 119/126] compute hash from payload bytes and compare with transaction hash field Signed-off-by: David Hernando --- Casper.Network.SDK/Types/TransactionV1.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Casper.Network.SDK/Types/TransactionV1.cs b/Casper.Network.SDK/Types/TransactionV1.cs index 17269fe..c11eaed 100644 --- a/Casper.Network.SDK/Types/TransactionV1.cs +++ b/Casper.Network.SDK/Types/TransactionV1.cs @@ -131,8 +131,16 @@ public void AddApproval(Approval approval) /// false if the validation of hash is not successful public bool ValidateHashes(out string message) { - var computedHash = this.ToBytes(); - if (!Hex.Decode(this.Hash).SequenceEqual(computedHash)) + var payloadBytes = this.Payload.ToBytes(); + var blake2BDigest = new Org.BouncyCastle.Crypto.Digests.Blake2bDigest(256); + + blake2BDigest.BlockUpdate(payloadBytes, 0, payloadBytes.Length); + + var hash = new byte[blake2BDigest.GetDigestSize()]; + blake2BDigest.DoFinal(hash, 0); + + var computedHash = Hex.ToHexString(hash); + if (!this.Hash.Equals(computedHash, StringComparison.InvariantCultureIgnoreCase)) { message = "Computed hash does not match value in transaction object." + $"Expected: '{this.Hash}'. " + @@ -141,7 +149,7 @@ public bool ValidateHashes(out string message) } message = ""; - return false; + return true; } /// From 20e2d200f2c3198b73ff497a09edbb7eb6f7c71f Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 17 Mar 2025 09:44:50 +0100 Subject: [PATCH 120/126] Adde refund and current_price parsing. Signed-off-by: David Hernando --- .../RPCResponses/GetTransactionTest.cs | 5 ++++ .../TestData/get-transaction-deploy-v200.json | 2 ++ .../TestData/get-transaction-stored-v200.json | 2 ++ Casper.Network.SDK/Types/ExecutionResult.cs | 30 +++++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs index 9983185..f29905e 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetTransactionTest.cs @@ -2,6 +2,7 @@ using Casper.Network.SDK.JsonRpc.ResultTypes; using Casper.Network.SDK.Types; using NUnit.Framework; +using Org.BouncyCastle.Math; using Org.BouncyCastle.Utilities.Encoders; namespace NetCasperTest.RPCResponses @@ -36,6 +37,8 @@ public void GetTransactionWithDeployHashTest_v200() AssertExtensions.IsHash(result.ExecutionInfo.BlockHash); Assert.IsTrue(result.ExecutionInfo.BlockHeight > 0); Assert.IsNotNull(result.ExecutionInfo.ExecutionResult); + Assert.AreEqual(1, result.ExecutionInfo.ExecutionResult.CurrentGasPrice); + Assert.AreEqual("30000", result.ExecutionInfo.ExecutionResult.Refund.ToString()); } [Test] @@ -136,6 +139,8 @@ public void GetTransactionWithStoredTransactionTest_v200() AssertExtensions.IsHash(result.ExecutionInfo.BlockHash); Assert.IsTrue(result.ExecutionInfo.BlockHeight > 0); Assert.IsNotNull(result.ExecutionInfo.ExecutionResult); + Assert.AreEqual(2, result.ExecutionInfo.ExecutionResult.CurrentGasPrice); + Assert.AreEqual("2123123123", result.ExecutionInfo.ExecutionResult.Refund.ToString()); } [Test] diff --git a/Casper.Network.SDK.Test/TestData/get-transaction-deploy-v200.json b/Casper.Network.SDK.Test/TestData/get-transaction-deploy-v200.json index 05ef8fc..069c579 100644 --- a/Casper.Network.SDK.Test/TestData/get-transaction-deploy-v200.json +++ b/Casper.Network.SDK.Test/TestData/get-transaction-deploy-v200.json @@ -81,6 +81,8 @@ "limit": "10000", "consumed": "10000", "cost": "10000", + "refund": "30000", + "current_price": 1, "payment": [], "transfers": [ { diff --git a/Casper.Network.SDK.Test/TestData/get-transaction-stored-v200.json b/Casper.Network.SDK.Test/TestData/get-transaction-stored-v200.json index db6c83d..283646a 100644 --- a/Casper.Network.SDK.Test/TestData/get-transaction-stored-v200.json +++ b/Casper.Network.SDK.Test/TestData/get-transaction-stored-v200.json @@ -75,6 +75,8 @@ "limit": "3000000000", "consumed": "1241925", "cost": "3000000000", + "refund": "2123123123", + "current_price": 2, "transfers": [], "size_estimate": 591, "effects": [ diff --git a/Casper.Network.SDK/Types/ExecutionResult.cs b/Casper.Network.SDK/Types/ExecutionResult.cs index 3e8fefd..ea7165b 100644 --- a/Casper.Network.SDK/Types/ExecutionResult.cs +++ b/Casper.Network.SDK/Types/ExecutionResult.cs @@ -53,6 +53,8 @@ public static explicit operator ExecutionResult(ExecutionResultV1 executionResul ErrorMessage = executionResult.ErrorMessage, // Transfers = executionResult.Transfers, Cost = executionResult.Cost, + Refund = 0, + CurrentGasPrice = 1, Limit = 0, Consumed = 0, Effect = v2Effect, @@ -81,6 +83,19 @@ public static explicit operator ExecutionResult(ExecutionResultV1 executionResul [JsonPropertyName("cost")] [JsonConverter(typeof(BigIntegerConverter))] public BigInteger Cost { get; init; } + + /// + /// How much was refunded (if any) + /// + [JsonPropertyName("refund")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Refund { get; init; } + + /// + /// The gas price of the era. + /// + [JsonPropertyName("current_price")] + public UInt16 CurrentGasPrice { get; init; } /// /// If there is no error message, this execution was processed successfully. If there is an error message, this @@ -150,6 +165,8 @@ public override ExecutionResult Read( ErrorMessage = erv2.ErrorMessage, Transfers = erv2.Transfers, Cost = erv2.Cost, + Refund = erv2.Refund, + CurrentGasPrice = erv2.CurrentGasPrice, Limit = erv2.Limit, Consumed = erv2.Consumed, Effect = erv2.Effect, @@ -342,6 +359,19 @@ internal class ExecutionResultV2 [JsonConverter(typeof(BigIntegerConverter))] public BigInteger Cost { get; init; } + /// + /// How much was refunded (if any) + /// + [JsonPropertyName("refund")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Refund { get; init; } + + /// + /// The gas price of the era. + /// + [JsonPropertyName("current_price")] + public UInt16 CurrentGasPrice { get; init; } + /// /// If there is no error message, this execution was processed successfully. If there is an error message, this /// execution failed to fully process for the stated reason. From 74a5b233362a944502ccb5213371fdca0d84adce Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 17 Mar 2025 09:55:56 +0100 Subject: [PATCH 121/126] handle entity-contract prefix in message keys Signed-off-by: David Hernando --- Casper.Network.SDK.Test/GlobalStateKeyTest.cs | 6 +++--- Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Casper.Network.SDK.Test/GlobalStateKeyTest.cs b/Casper.Network.SDK.Test/GlobalStateKeyTest.cs index e05e781..02f4bc2 100644 --- a/Casper.Network.SDK.Test/GlobalStateKeyTest.cs +++ b/Casper.Network.SDK.Test/GlobalStateKeyTest.cs @@ -541,7 +541,7 @@ public void MessageTopicKeyTest() { var hashAddr = "55d4a6915291da12afded37fa5bc01f0803a2f0faf6acb7ec4c7ca6ab76f3330"; var topicStr = "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1"; - var msgKeyStr = $"message-topic-{hashAddr}-{topicStr}"; + var msgKeyStr = $"message-topic-entity-contract-{hashAddr}-{topicStr}"; var key = GlobalStateKey.FromString(msgKeyStr); Assert.IsNotNull(key); @@ -562,8 +562,8 @@ public void MessageIndexKeyTest() { var hashAddr = "55d4a6915291da12afded37fa5bc01f0803a2f0faf6acb7ec4c7ca6ab76f3330"; var topicStr = "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1"; - var indexStr = "f"; - var msgKeyStr = $"message-{hashAddr}-{topicStr}-{indexStr}"; + var indexStr = "0f"; + var msgKeyStr = $"message-entity-contract-{hashAddr}-{topicStr}-{indexStr}"; var key = GlobalStateKey.FromString(msgKeyStr); Assert.IsNotNull(key); diff --git a/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs index 0c42400..485c92b 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs @@ -27,6 +27,9 @@ public MessageKey(string key) : base(key) if (key.StartsWith(TOPIC_PREFIX)) { key = key.Substring(TOPIC_PREFIX.Length); + key = key.Replace("entity-contract-", ""); + key = key.Replace("entity-system-", ""); + key = key.Replace("entity-account-", ""); var parts = key.Split('-'); if(parts.Length == 2) { @@ -38,6 +41,9 @@ public MessageKey(string key) : base(key) } else { + key = key.Replace("entity-contract-", ""); + key = key.Replace("entity-system-", ""); + key = key.Replace("entity-account-", ""); var parts = key.Split('-'); if (parts.Length == 3) { From a036604e3b38188150c75c6d03a769a814d693a3 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Mon, 17 Mar 2025 11:55:39 +0100 Subject: [PATCH 122/126] replace HashAddr with AddressableEntity key. Replace AddressableEntityHash with AddressableEntitye Signed-off-by: David Hernando --- Casper.Network.SDK.Test/GlobalStateKeyTest.cs | 16 +++---- .../RPCResponses/GetPackageResultTest.cs | 6 ++- .../get-package-result-package-v200.json | 2 +- .../Types/GlobalStateKey/MessageKey.cs | 47 +++++++------------ Casper.Network.SDK/Types/Package.cs | 7 +-- 5 files changed, 36 insertions(+), 42 deletions(-) diff --git a/Casper.Network.SDK.Test/GlobalStateKeyTest.cs b/Casper.Network.SDK.Test/GlobalStateKeyTest.cs index 02f4bc2..11f01a9 100644 --- a/Casper.Network.SDK.Test/GlobalStateKeyTest.cs +++ b/Casper.Network.SDK.Test/GlobalStateKeyTest.cs @@ -539,9 +539,9 @@ public void BalanceHoldKeyTest() [Test] public void MessageTopicKeyTest() { - var hashAddr = "55d4a6915291da12afded37fa5bc01f0803a2f0faf6acb7ec4c7ca6ab76f3330"; + var hashAddr = "entity-contract-55d4a6915291da12afded37fa5bc01f0803a2f0faf6acb7ec4c7ca6ab76f3330"; var topicStr = "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1"; - var msgKeyStr = $"message-topic-entity-contract-{hashAddr}-{topicStr}"; + var msgKeyStr = $"message-topic-{hashAddr}-{topicStr}"; var key = GlobalStateKey.FromString(msgKeyStr); Assert.IsNotNull(key); @@ -549,8 +549,8 @@ public void MessageTopicKeyTest() var messageKey = key as MessageKey; Assert.IsNotNull(messageKey); - Assert.IsNotNull(messageKey.HashAddr); - Assert.AreEqual(hashAddr, messageKey.HashAddr); + Assert.IsNotNull(messageKey.AddressableEntity); + Assert.AreEqual(hashAddr, messageKey.AddressableEntity.ToString()); Assert.AreEqual(topicStr, messageKey.TopicHash); Assert.IsFalse(messageKey.Index.HasValue); Assert.AreEqual(msgKeyStr, messageKey.ToString().ToLower()); @@ -560,10 +560,10 @@ public void MessageTopicKeyTest() [Test] public void MessageIndexKeyTest() { - var hashAddr = "55d4a6915291da12afded37fa5bc01f0803a2f0faf6acb7ec4c7ca6ab76f3330"; + var hashAddr = "entity-contract-55d4a6915291da12afded37fa5bc01f0803a2f0faf6acb7ec4c7ca6ab76f3330"; var topicStr = "5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1"; var indexStr = "0f"; - var msgKeyStr = $"message-entity-contract-{hashAddr}-{topicStr}-{indexStr}"; + var msgKeyStr = $"message-{hashAddr}-{topicStr}-{indexStr}"; var key = GlobalStateKey.FromString(msgKeyStr); Assert.IsNotNull(key); @@ -571,8 +571,8 @@ public void MessageIndexKeyTest() var messageKey = key as MessageKey; Assert.IsNotNull(messageKey); - Assert.IsNotNull(messageKey.HashAddr); - Assert.AreEqual(hashAddr, messageKey.HashAddr); + Assert.IsNotNull(messageKey.AddressableEntity); + Assert.AreEqual(hashAddr, messageKey.AddressableEntity.ToString()); Assert.AreEqual(topicStr, messageKey.TopicHash); Assert.IsTrue(messageKey.Index.HasValue); Assert.AreEqual(15, messageKey.Index.Value); diff --git a/Casper.Network.SDK.Test/RPCResponses/GetPackageResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetPackageResultTest.cs index b42a2a2..a8691ac 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetPackageResultTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetPackageResultTest.cs @@ -26,6 +26,9 @@ public void GetPackageResultContractPackageTest_v200() Assert.AreEqual(4, result.ContractPackage.DisabledVersions[0].Version); Assert.AreEqual("contract-25aa2d3cc62a302746c08ae885454d6e8a9c8609aaa7468b24284e5d29c5d2f1", result.ContractPackage.Versions[0].Hash); Assert.AreEqual(LockStatus.Unlocked, result.ContractPackage.LockStatus); + + var package = Package.FromContractPackage(result.ContractPackage); + Assert.AreEqual("entity-contract-25aa2d3cc62a302746c08ae885454d6e8a9c8609aaa7468b24284e5d29c5d2f1", package.Versions[0].AddressableEntity.ToString()); } [Test] public void GetPackageResultPackageTest_v200() @@ -41,7 +44,8 @@ public void GetPackageResultPackageTest_v200() Assert.AreEqual(1, result.Package.Versions.Count); Assert.AreEqual(2, result.Package.Versions[0].EntityVersion.ProtocolVersionMajor); Assert.AreEqual(1, result.Package.Versions[0].EntityVersion.Version); - Assert.AreEqual("addressable-entity-e51af99d88fd26a282de00271c49a6c256232b344aa7907d2c8603b2bd5217c9", result.Package.Versions[0].AddressableEntityHash); + Assert.AreEqual("entity-contract-e51af99d88fd26a282de00271c49a6c256232b344aa7907d2c8603b2bd5217c9", result.Package.Versions[0].AddressableEntity.ToString()); + Assert.AreEqual(EntityKindEnum.Contract, result.Package.Versions[0].AddressableEntity.Kind); Assert.AreEqual(LockStatus.Unlocked, result.Package.LockStatus); } } diff --git a/Casper.Network.SDK.Test/TestData/get-package-result-package-v200.json b/Casper.Network.SDK.Test/TestData/get-package-result-package-v200.json index 20776d6..7ef2028 100644 --- a/Casper.Network.SDK.Test/TestData/get-package-result-package-v200.json +++ b/Casper.Network.SDK.Test/TestData/get-package-result-package-v200.json @@ -8,7 +8,7 @@ "protocol_version_major": 2, "entity_version": 1 }, - "addressable_entity_hash": "addressable-entity-e51af99d88fd26a282de00271c49a6c256232b344aa7907d2c8603b2bd5217c9" + "entity_addr": "entity-contract-e51af99d88fd26a282de00271c49a6c256232b344aa7907d2c8603b2bd5217c9" } ], "disabled_versions": [], diff --git a/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs b/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs index 485c92b..1be7f6f 100644 --- a/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs +++ b/Casper.Network.SDK/Types/GlobalStateKey/MessageKey.cs @@ -10,8 +10,8 @@ public class MessageKey : GlobalStateKey private const string KEY_PREFIX = "message-"; private const string TOPIC_PREFIX = "topic-"; - public string HashAddr { get; init; } - + public AddressableEntityKey AddressableEntity { get; init; } + public string TopicHash { get; init; } public UInt32? Index { get; init; } @@ -27,35 +27,25 @@ public MessageKey(string key) : base(key) if (key.StartsWith(TOPIC_PREFIX)) { key = key.Substring(TOPIC_PREFIX.Length); - key = key.Replace("entity-contract-", ""); - key = key.Replace("entity-system-", ""); - key = key.Replace("entity-account-", ""); var parts = key.Split('-'); - if(parts.Length == 2) - { - HashAddr = parts[0]; - TopicHash = parts[1]; - } - else - throw new Exception("Key not valid. It should have a hash address and a topic hash."); + if (parts.Length != 4) + throw new Exception("Key not valid. It should have an entity address and a topic hash."); + + AddressableEntity = new AddressableEntityKey($"{parts[0]}-{parts[1]}-{parts[2]}"); + TopicHash = parts[3]; } else { - key = key.Replace("entity-contract-", ""); - key = key.Replace("entity-system-", ""); - key = key.Replace("entity-account-", ""); var parts = key.Split('-'); - if (parts.Length == 3) - { - HashAddr = parts[0]; - TopicHash = parts[1]; - - if(parts[2].Length == 0) - throw new Exception("Key not valid. Expected a non-empty message index."); - Index = Convert.ToUInt32(parts[2], 16); - } - else - throw new Exception("Key not valid. It should have a hash address, a topic hash, and a message index."); + if (parts.Length != 5) + throw new Exception("Key not valid. It should have an entity address, a topic hash, and a message index."); + + AddressableEntity = new AddressableEntityKey($"{parts[0]}-{parts[1]}-{parts[2]}"); + TopicHash = parts[3]; + + if(parts[4].Length == 0) + throw new Exception("Key not valid. Expected a non-empty message index."); + Index = Convert.ToUInt32(parts[4], 16); } } @@ -66,8 +56,7 @@ public MessageKey(byte[] key) : base(null) var ms = new MemoryStream(key); var reader = new BinaryReader(ms); - var hash = reader.ReadBytes(32); - HashAddr = Hex.ToHexString(hash); + AddressableEntity = new AddressableEntityKey(reader); var topic = reader.ReadBytes(32); TopicHash = Hex.ToHexString(topic); @@ -78,7 +67,7 @@ public MessageKey(byte[] key) : base(null) Key = KEY_PREFIX + (Index.HasValue ? "" : TOPIC_PREFIX) + - HashAddr + "-" + + AddressableEntity.ToString() + "-" + TopicHash + (Index.HasValue ? "-" + Index.Value.ToString("x") : ""); } diff --git a/Casper.Network.SDK/Types/Package.cs b/Casper.Network.SDK/Types/Package.cs index 6ef0efa..3715f1b 100644 --- a/Casper.Network.SDK/Types/Package.cs +++ b/Casper.Network.SDK/Types/Package.cs @@ -47,8 +47,9 @@ public class EntityVersionAndHash /// /// The hex-encoded address of the addressable entity. /// - [JsonPropertyName("addressable_entity_hash")] - public string AddressableEntityHash { get; init; } + [JsonPropertyName("entity_addr")] + [JsonConverter(typeof(AddressableEntityKey.AddressableEntityKeyConverter))] + public AddressableEntityKey AddressableEntity { get; init; } } public class NamedUserGroup @@ -107,7 +108,7 @@ public static Package FromContractPackage(ContractPackage contractPackage) { EntityVersion = new EntityVersion() { Version = v.Version, ProtocolVersionMajor = v.ProtocolVersionMajor }, - AddressableEntityHash = v.Hash, + AddressableEntity = new AddressableEntityKey("entity-contract-" + v.Hash.Replace("contract-", "")), }).ToList(), Groups = contractPackage.Groups.Select(g => new NamedUserGroup() { From b1fbd8fd847dc3be32f6fc13311a6987d5a1fdc6 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 20 Mar 2025 10:32:18 +0100 Subject: [PATCH 123/126] Fixes a bug where events are emitted 3 times in Casper 2.0. Removed type TransactionAccepted as not necessary. Fixed a bug on GetValidatorBid() where a state root hash was being queried instead of a block hash. Signed-off-by: David Hernando --- Casper.Network.SDK/NetCasperClient.cs | 2 +- Casper.Network.SDK/SSE/SSEvent.cs | 2 +- Casper.Network.SDK/SSE/ServerEventsClient.cs | 11 +++++++++++ Casper.Network.SDK/SSE/TransactionAccepted.cs | 10 ---------- Casper.Network.SDK/Types/Transaction.cs | 1 + Docs/Examples/AwaitEvents/Program.cs | 3 ++- 6 files changed, 16 insertions(+), 13 deletions(-) delete mode 100644 Casper.Network.SDK/SSE/TransactionAccepted.cs diff --git a/Casper.Network.SDK/NetCasperClient.cs b/Casper.Network.SDK/NetCasperClient.cs index ee9006b..8ee0ca8 100644 --- a/Casper.Network.SDK/NetCasperClient.cs +++ b/Casper.Network.SDK/NetCasperClient.cs @@ -974,7 +974,7 @@ public static async Task GetNodeMetrics(string host, int port) public async Task> GetValidatorBid(PublicKey validator, string blockHash = null) { var bidAddr = BidAddrKey.FromValidatorKey(new AccountHashKey(validator)); - return await QueryGlobalState(bidAddr, blockHash); + return await QueryGlobalStateWithBlockHash(bidAddr, blockHash); } public void Dispose() diff --git a/Casper.Network.SDK/SSE/SSEvent.cs b/Casper.Network.SDK/SSE/SSEvent.cs index 2464cc0..d7a0245 100644 --- a/Casper.Network.SDK/SSE/SSEvent.cs +++ b/Casper.Network.SDK/SSE/SSEvent.cs @@ -56,7 +56,7 @@ public T Parse() if (typeof(T) == typeof(Step)) json = this.Result.GetProperty("Step").GetRawText(); - if (typeof(T) == typeof(TransactionAccepted)) + if (typeof(T) == typeof(Transaction)) json = this.Result.GetProperty("TransactionAccepted").GetRawText(); if (typeof(T) == typeof(TransactionProcessed)) diff --git a/Casper.Network.SDK/SSE/ServerEventsClient.cs b/Casper.Network.SDK/SSE/ServerEventsClient.cs index c1bbee3..c9ea217 100644 --- a/Casper.Network.SDK/SSE/ServerEventsClient.cs +++ b/Casper.Network.SDK/SSE/ServerEventsClient.cs @@ -192,6 +192,17 @@ public bool RemoveEventCallback(EventType eventType, string name) private void UpdateChannels(EventType eventType, int startFrom = int.MaxValue) { + if (_nodeVersion == 2) + { + _channels = new Dictionary() + { + { ChannelType.Main, startFrom }, + }; + return; + } + + // for _nodeVersion == 1, we need to listen to different channels + // var ch = new Dictionary(); foreach (var cb in _callbacks) { diff --git a/Casper.Network.SDK/SSE/TransactionAccepted.cs b/Casper.Network.SDK/SSE/TransactionAccepted.cs deleted file mode 100644 index b3235f3..0000000 --- a/Casper.Network.SDK/SSE/TransactionAccepted.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json.Serialization; -using Casper.Network.SDK.Types; - -namespace Casper.Network.SDK.SSE -{ - [JsonConverter(typeof(Transaction.TransactionConverter))] - public class TransactionAccepted : Transaction - { - } -} \ No newline at end of file diff --git a/Casper.Network.SDK/Types/Transaction.cs b/Casper.Network.SDK/Types/Transaction.cs index 8ed6884..a078ef0 100644 --- a/Casper.Network.SDK/Types/Transaction.cs +++ b/Casper.Network.SDK/Types/Transaction.cs @@ -26,6 +26,7 @@ internal class TransactionCompat /// /// A versioned wrapper for a TransactionV1 or Deploy. /// + [JsonConverter(typeof(TransactionConverter))] public partial class Transaction { protected TransactionVersion _version; diff --git a/Docs/Examples/AwaitEvents/Program.cs b/Docs/Examples/AwaitEvents/Program.cs index 155a0d1..c56775d 100644 --- a/Docs/Examples/AwaitEvents/Program.cs +++ b/Docs/Examples/AwaitEvents/Program.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Casper.Network.SDK; using Casper.Network.SDK.SSE; +using Casper.Network.SDK.Types; namespace Casper.NET.SDK.Examples { @@ -42,7 +43,7 @@ public static void ListenEvents(int startFrom) } else if (evt.EventType == EventType.TransactionAccepted) { - var transaction = evt.Parse(); + var transaction = evt.Parse(); Console.WriteLine("TransactionAccepted: " + transaction.Hash); } else if (evt.EventType == EventType.TransactionProcessed) From 67d12377f88c69db5eb2cd15825ce762747d4a63 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 20 Mar 2025 10:52:45 +0100 Subject: [PATCH 124/126] Bump up version. Update CHANGELOG. Signed-off-by: David Hernando --- CHANGELOG.md | 20 ++++++++++++++++++++ Casper.Network.SDK/Casper.Network.SDK.csproj | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2245945..908f6f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,25 @@ All notable changes to this project will be documented in this file. The format [comment]: <> (Fixed: any bug fixes) [comment]: <> (Security: in case of vulnerabilities) +## [3.0.0-beta3] + +### Added + +* The `TransactionBuilder` now accepts amount as `ulong` type for the `Payment()` method. +* New `GlobalStateKey.FromValidatorKey()` to get the bid address key of a validator. +* New `GetNodeVersion()` in the main client class as a convenient method to check node version. +* New `GetValidatorBid()` method to recover the bid information of a validator. + +### Changed + +* `MessageKey` contains an `AddressableEntity` property instead of a `HashAddr` to represent the related contract entity. +* `GetAuctionInfo()` now uses the new `state_get_auction_info_v2` RPC method in Casper 2.0. + +### Fixed + +* [#99](https://github.com/make-software/casper-net-sdk/issues/99) SSE Listener emits the same event 3 times when listening to EventType.All +* Fixed the `TransactionV1.ValidateHashes()` method to properly verify the hashes. + ## [3.0.0-beta2] ### Added @@ -163,6 +182,7 @@ This new type permits to parse correctly the value `"00"` used for system blocks ### Added * Initial release of Casper .NET SDK. +[3.0.0-beta3]: https://github.com/make-software/casper-net-sdk/releases/tag/v3.0.0-beta3 [3.0.0-beta2]: https://github.com/make-software/casper-net-sdk/releases/tag/v3.0.0-beta2 [3.0.0-beta1]: https://github.com/make-software/casper-net-sdk/releases/tag/v3.0.0-beta1 [2.3.0]: https://github.com/make-software/casper-net-sdk/releases/tag/v2.3.0 diff --git a/Casper.Network.SDK/Casper.Network.SDK.csproj b/Casper.Network.SDK/Casper.Network.SDK.csproj index 26645a7..b26c171 100644 --- a/Casper.Network.SDK/Casper.Network.SDK.csproj +++ b/Casper.Network.SDK/Casper.Network.SDK.csproj @@ -5,8 +5,8 @@ 9.0 CS1591 3.0.0.0 - 3.0.1-beta2 - 3.0.1-beta2 + 3.0.0-beta3 + 3.0.0-beta3 Casper.Network.SDK make-software https://github.com/make-software/casper-net-sdk From 3f61e95f989ec6c224085e52a200709bf560bc06 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 20 Mar 2025 10:57:42 +0100 Subject: [PATCH 125/126] Fix test data Signed-off-by: David Hernando --- .../get-transaction-deploy-stored-v200.json | 14 +++++++------- .../TestData/get-transaction-session-v200.json | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Casper.Network.SDK.Test/TestData/get-transaction-deploy-stored-v200.json b/Casper.Network.SDK.Test/TestData/get-transaction-deploy-stored-v200.json index 404246f..54766a3 100644 --- a/Casper.Network.SDK.Test/TestData/get-transaction-deploy-stored-v200.json +++ b/Casper.Network.SDK.Test/TestData/get-transaction-deploy-stored-v200.json @@ -141,19 +141,19 @@ "kind": "Identity" }, { - "key": "message-topic-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", "kind": "Identity" }, { - "key": "message-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-1", + "key": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-1", "kind": { - "Prune": "message-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-1" + "Prune": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-1" } }, { - "key": "message-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-2", + "key": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-2", "kind": { - "Prune": "message-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-2" + "Prune": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-2" } }, { @@ -161,7 +161,7 @@ "kind": "Identity" }, { - "key": "message-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-0", + "key": "message-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-0", "kind": { "Write": { "Message": "message-checksum-4e8fa8422d6022df02a2d41568e25ebfbc9d7d098f8c31f42e4493523627f0f4" @@ -169,7 +169,7 @@ } }, { - "key": "message-topic-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "key": "message-topic-entity-contract-d8850e3227ce97cdb21a3e266fe360a196ec6c97058ee1ea242da0d03aa51c58-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", "kind": { "Write": { "MessageTopic": { diff --git a/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json b/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json index feeffa6..60a108d 100644 --- a/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json +++ b/Casper.Network.SDK.Test/TestData/get-transaction-session-v200.json @@ -241,7 +241,7 @@ "kind": "Identity" }, { - "key": "message-topic-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "key": "message-topic-entity-contract-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", "kind": { "Write": { "MessageTopic": { @@ -797,11 +797,11 @@ "kind": "Identity" }, { - "key": "message-topic-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "key": "message-topic-entity-contract-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", "kind": "Identity" }, { - "key": "message-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-0", + "key": "message-entity-contract-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1-0", "kind": { "Write": { "Message": "message-checksum-4f9c3b0e96c2fd77a8a0d11c123c23ee4251c183707307205ae02ecd29c3385e" @@ -809,7 +809,7 @@ } }, { - "key": "message-topic-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", + "key": "message-topic-entity-contract-783ac85c80ca97c0a3f699fedbde796c548e3cd0f8d015c3f3158090272eebd4-5721a6d9d7a9afe5dfdb35276fb823bed0f825350e4d865a5ec0110c380de4e1", "kind": { "Write": { "MessageTopic": { From 76cdef078fcbc1fcf3a7f54f1e2567e0e1bb7a75 Mon Sep 17 00:00:00 2001 From: David Hernando Date: Thu, 20 Mar 2025 18:21:56 +0100 Subject: [PATCH 126/126] Added Unbond type to BidKind type Signed-off-by: David Hernando --- .../RPCResponses/GetAuctionInfoResultTest.cs | 7 +- .../TestData/get-auction-info-v200.json | 21 +++++- Casper.Network.SDK/Types/BidKind.cs | 68 +++++++++++++++++++ 3 files changed, 94 insertions(+), 2 deletions(-) diff --git a/Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs b/Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs index a47107d..cb402dd 100644 --- a/Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs +++ b/Casper.Network.SDK.Test/RPCResponses/GetAuctionInfoResultTest.cs @@ -55,7 +55,7 @@ public void GetBlockResultTest_v200() Assert.AreEqual("017536433a73f7562526f3e9fcb8d720428ae2d28788a9909f3c6f637a9d848a4b", result.AuctionState.EraValidators[2].ValidatorWeights[3].PublicKey.ToString().ToLower()); Assert.AreEqual(BigInteger.Parse("2030445261010189498"), result.AuctionState.EraValidators[2].ValidatorWeights[3].Weight); - Assert.AreEqual(4, result.AuctionState.Bids.Count); + Assert.AreEqual(5, result.AuctionState.Bids.Count); Assert.IsNotNull(result.AuctionState.Bids[0].Validator); Assert.AreEqual("01358a7e107668ae2eb092dcfbeb97d2ec3cc8354d2a77bc8f232fff6630a826c3", result.AuctionState.Bids[0].Validator.PublicKey.ToString().ToLower()); @@ -73,6 +73,11 @@ public void GetBlockResultTest_v200() Assert.AreEqual(BigInteger.Parse("1676515877735"), result.AuctionState.Bids[3].Delegator.StakedAmount); Assert.IsNull(result.AuctionState.Bids[3].Delegator.DelegatorKind.PublicKey); Assert.AreEqual("8af7b77811970792f98b806779dfc0d1a9fef5bad205c6be8bb884210d7d323c", result.AuctionState.Bids[3].Delegator.DelegatorKind.Purse); + + Assert.IsNotNull(result.AuctionState.Bids[4].Unbond); + Assert.AreEqual("0106ca7c39cd272dbf21a86eeb3b36b7c26e2e9b94af64292419f7862936bca2ca", result.AuctionState.Bids[4].Unbond.Validator.ToString().ToLower()); + Assert.AreEqual(1, result.AuctionState.Bids[4].Unbond.Eras.Count); + Assert.AreEqual("uref-a3c42d782f460513f2e1ee5569208d9efa80b34adf9736e011fdf1f5ca91bc08-007", result.AuctionState.Bids[4].Unbond.Eras[0].BondingPurse.ToString()); } } } \ No newline at end of file diff --git a/Casper.Network.SDK.Test/TestData/get-auction-info-v200.json b/Casper.Network.SDK.Test/TestData/get-auction-info-v200.json index 730767f..511922f 100644 --- a/Casper.Network.SDK.Test/TestData/get-auction-info-v200.json +++ b/Casper.Network.SDK.Test/TestData/get-auction-info-v200.json @@ -75,7 +75,7 @@ "Validator": { "validator_public_key": "01358a7e107668ae2eb092dcfbeb97d2ec3cc8354d2a77bc8f232fff6630a826c3", "bonding_purse": "uref-027d909fa0818f8b426b905795f608a6301168476b8013d7b3f682786796096f-007", - "staked_amount": "2500000000", + "staked_amount": "2500000000", "delegation_rate": 1, "vesting_schedule": null, "inactive": true, @@ -128,6 +128,25 @@ "vesting_schedule": null } } + }, + { + "public_key": "0106ca7c39cd272dbf21a86eeb3b36b7c26e2e9b94af64292419f7862936bca2ca", + "bid": { + "Unbond": { + "validator_public_key": "0106ca7c39cd272dbf21a86eeb3b36b7c26e2e9b94af64292419f7862936bca2ca", + "unbond_kind": { + "DelegatedPublicKey": "0154161886e8bcfea05f79efd7e8bfef2ed5a36c18d7d86d748fb21236f9e06d03" + }, + "eras": [ + { + "bonding_purse": "uref-a3c42d782f460513f2e1ee5569208d9efa80b34adf9736e011fdf1f5ca91bc08-007", + "era_of_creation": 17243, + "amount": "500000000000", + "new_validator": null + } + ] + } + } } ] } diff --git a/Casper.Network.SDK/Types/BidKind.cs b/Casper.Network.SDK/Types/BidKind.cs index cc9cba7..3ae449c 100644 --- a/Casper.Network.SDK/Types/BidKind.cs +++ b/Casper.Network.SDK/Types/BidKind.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Numerics; using System.Text.Json.Serialization; using Casper.Network.SDK.Converters; @@ -55,6 +56,67 @@ public class ValidatorCredit [JsonConverter(typeof(PublicKey.PublicKeyConverter))] public PublicKey Validator { get; init; } } + + public class UnbondKind + { + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey Validator { get; init; } + + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey DelegatedPublicKey { get; init; } + + public string DelegatedPurse { get; init; } + } + + public class UnbondEra + { + /// + /// The purse that was used for bonding. + /// + [JsonPropertyName("bonding_purse")] + [JsonConverter(typeof(GlobalStateKey.GlobalStateKeyConverter))] + public URef BondingPurse { get; init; } + + /// + /// Era in which this unbonding request was created. + /// + [JsonPropertyName("era_of_creation")] + public ulong EraOfCreation { get; init; } + + /// + /// Unbonding Amount + /// + [JsonPropertyName("amount")] + [JsonConverter(typeof(BigIntegerConverter))] + public BigInteger Amount { get; init; } + + /// + /// The validator public key to re-delegate to. + /// + [JsonPropertyName("new_validator")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey NewValidator { get; init; } + } + + + public class Unbond + { + /// + /// Validator public key. + /// + [JsonPropertyName("validator_public_key")] + [JsonConverter(typeof(PublicKey.PublicKeyConverter))] + public PublicKey Validator { get; init; } + + /// + /// Unbond kind. + /// + [JsonPropertyName("unbond_kind")] + public UnbondKind Kind { get; init; } + + [JsonPropertyName("eras")] + public List Eras { get; init; } + } /// /// Auction bid variants. @@ -98,5 +160,11 @@ public class BidKind /// [JsonPropertyName("Reservation")] public Reservation Reservation { get; init; } + + /// + /// A bid record containing Unbond information + /// + [JsonPropertyName("Unbond")] + public Unbond Unbond { get; init; } } }