-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #89 from Concordium/init-contract
Add Init Contract transaction
- Loading branch information
Showing
13 changed files
with
555 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
## Unreleased changes | ||
- Added | ||
- New transaction `InitContract` | ||
|
||
## 4.3.1 | ||
- Added | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\..\src\Concordium.Sdk.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Common\Common.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="CommandLineParser" Version="2.9.1" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
using System.Globalization; | ||
using CommandLine; | ||
using Concordium.Sdk.Client; | ||
using Concordium.Sdk.Types; | ||
using Concordium.Sdk.Wallets; | ||
|
||
// We disable these warnings since CommandLine needs to set properties in options | ||
// but we don't want to give default values. | ||
#pragma warning disable CS8618 | ||
|
||
namespace InitContract; | ||
|
||
internal sealed class InitContractOptions | ||
{ | ||
[Option( | ||
'k', | ||
"keys", | ||
HelpText = "Path to a file with contents that is in the Concordium browser wallet key export format.", | ||
Required = true | ||
)] | ||
public string WalletKeysFile { get; set; } | ||
[Option(HelpText = "URL representing the endpoint where the gRPC V2 API is served.", | ||
Default = "http://grpc.testnet.concordium.com:20000/")] | ||
public string Endpoint { get; set; } | ||
[Option('a', "amount", HelpText = "Amount of CCD to deposit.", Default = 0)] | ||
public ulong Amount { get; set; } | ||
|
||
[Option('m', "module-ref", HelpText = "The module reference of the smart contract.", Required = true)] | ||
public string ModuleRef { get; set; } | ||
|
||
[Option('i', "init-name", HelpText = "The init_name of the module.", Required = true)] | ||
public string InitName { get; set; } | ||
|
||
[Option('e', "max-energy", HelpText = "The maximum energy to spend on the module.", Required = true)] | ||
public string MaxEnergy { get; set; } | ||
} | ||
|
||
public static class Program | ||
{ | ||
/// <summary> | ||
/// Example demonstrating how to submit a smart contract initialization | ||
/// transaction. | ||
/// | ||
/// The example assumes you have your account key information stored | ||
/// in the Concordium browser wallet key export format, and that a path | ||
/// pointing to it is supplied to it from the command line. | ||
/// </summary> | ||
public static async Task Main(string[] args) => | ||
await Parser.Default | ||
.ParseArguments<InitContractOptions>(args) | ||
.WithParsedAsync(Run); | ||
|
||
private static async Task Run(InitContractOptions o) | ||
{ | ||
// Read the account keys from a file. | ||
var walletData = File.ReadAllText(o.WalletKeysFile); | ||
var account = WalletAccount.FromWalletKeyExportFormat(walletData); | ||
|
||
// Construct the client. | ||
var clientOptions = new ConcordiumClientOptions | ||
{ | ||
Timeout = TimeSpan.FromSeconds(10) | ||
}; | ||
using var client = new ConcordiumClient(new Uri(o.Endpoint), clientOptions); | ||
|
||
// Create the init transaction. | ||
var successfulParse = ContractName.TryParse(o.InitName, out var parsed); | ||
if (!successfulParse) | ||
{ | ||
throw new ArgumentException("Error parsing (" + o.InitName + "): " + parsed.Error.ToString()); | ||
}; | ||
|
||
var amount = CcdAmount.FromCcd(o.Amount); | ||
var moduleRef = new ModuleReference(o.ModuleRef); | ||
var param = new Parameter(Array.Empty<byte>()); | ||
var maxEnergy = new EnergyAmount(uint.Parse(o.MaxEnergy, CultureInfo.InvariantCulture)); | ||
var payload = new Concordium.Sdk.Transactions.InitContract(amount, moduleRef, parsed.ContractName!, param); | ||
|
||
// Prepare the transaction for signing. | ||
var sender = account.AccountAddress; | ||
var sequenceNumber = client.GetNextAccountSequenceNumber(sender).Item1; | ||
var expiry = Expiry.AtMinutesFromNow(30); | ||
var preparedPayload = payload.Prepare(sender, sequenceNumber, expiry, maxEnergy); | ||
|
||
// Sign the transaction using the account keys. | ||
var signedTrx = preparedPayload.Sign(account); | ||
|
||
// Submit the transaction. | ||
var txHash = client.SendAccountTransaction(signedTrx); | ||
|
||
// Print the transaction hash. | ||
Console.WriteLine($"Successfully submitted init-contract transaction with hash {txHash}"); | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
using Concordium.Sdk.Types; | ||
|
||
namespace Concordium.Sdk.Transactions; | ||
|
||
/// <summary> | ||
/// Represents an "init_contract" transaction. | ||
/// | ||
/// Used for initializing deployed smart contracts. | ||
/// </summary> | ||
/// <param name="Amount">Deposit this amount of CCD.</param> | ||
/// <param name="ModuleRef">The smart contract module reference.</param> | ||
/// <param name="ContractName">The init name of the smart contract.</param> | ||
/// <param name="Parameter">The parameters for the smart contract.</param> | ||
public sealed record InitContract(CcdAmount Amount, ModuleReference ModuleRef, ContractName ContractName, Parameter Parameter) : AccountTransactionPayload | ||
{ | ||
/// <summary> | ||
/// The init contract transaction type to be used in the serialized payload. | ||
/// </summary> | ||
private const byte TransactionType = (byte)Types.TransactionType.InitContract; | ||
|
||
/// <summary> | ||
/// The minimum serialized length in the serialized payload. | ||
/// </summary> | ||
internal const uint MinSerializedLength = | ||
CcdAmount.BytesLength + | ||
Hash.BytesLength + // ModuleRef | ||
ContractName.MinSerializedLength + | ||
Parameter.MinSerializedLength; | ||
|
||
/// <summary> | ||
/// Prepares the account transaction payload for signing. | ||
/// </summary> | ||
/// <param name="sender">Address of the sender of the transaction.</param> | ||
/// <param name="sequenceNumber">Account sequence number to use for the transaction.</param> | ||
/// <param name="expiry">Expiration time of the transaction.</param> | ||
/// <param name="energy"> | ||
/// The amount of energy that can be used for contract execution. | ||
/// The base energy amount for transaction verification will be added to this cost. | ||
/// </param> | ||
public PreparedAccountTransaction Prepare( | ||
AccountAddress sender, | ||
AccountSequenceNumber sequenceNumber, | ||
Expiry expiry, | ||
EnergyAmount energy | ||
) => new(sender, sequenceNumber, expiry, energy, this); | ||
|
||
/// <summary> | ||
/// Gets the size (number of bytes) of the payload. | ||
/// </summary> | ||
internal override PayloadSize Size() => new( | ||
sizeof(TransactionType) + | ||
CcdAmount.BytesLength + | ||
Hash.BytesLength + // ModuleRef | ||
this.ContractName.SerializedLength() + | ||
this.Parameter.SerializedLength()); | ||
|
||
/// <summary> | ||
/// Deserialize a "InitContract" payload from a serialized byte array. | ||
/// </summary> | ||
/// <param name="bytes">The serialized InitContract payload.</param> | ||
/// <param name="output">Where to write the result of the operation.</param> | ||
public static bool TryDeserial(ReadOnlySpan<byte> bytes, out (InitContract? InitContract, string? Error) output) | ||
{ | ||
if (bytes.Length < MinSerializedLength) | ||
{ | ||
var msg = $"Invalid length in `InitContract.TryDeserial`. Expected at least {MinSerializedLength}, found {bytes.Length}"; | ||
output = (null, msg); | ||
return false; | ||
}; | ||
if (bytes[0] != TransactionType) | ||
{ | ||
var msg = $"Invalid transaction type in `InitContract.TryDeserial`. expected {TransactionType}, found {bytes[0]}"; | ||
output = (null, msg); | ||
return false; | ||
}; | ||
|
||
var remainingBytes = bytes[sizeof(TransactionType)..]; | ||
|
||
if (!CcdAmount.TryDeserial(remainingBytes, out var amount)) | ||
{ | ||
output = (null, amount.Error); | ||
return false; | ||
}; | ||
remainingBytes = remainingBytes[(int)CcdAmount.BytesLength..]; | ||
|
||
if (!ModuleReference.TryDeserial(remainingBytes, out var moduleRef)) | ||
{ | ||
output = (null, moduleRef.Error); | ||
return false; | ||
}; | ||
remainingBytes = remainingBytes[Hash.BytesLength..]; // ModuleRef | ||
|
||
if (!ContractName.TryDeserial(remainingBytes, out var name)) | ||
{ | ||
output = (null, name.Error); | ||
return false; | ||
}; | ||
remainingBytes = remainingBytes[(int)name.ContractName!.SerializedLength()..]; | ||
|
||
if (!Parameter.TryDeserial(remainingBytes, out var param)) | ||
{ | ||
output = (null, param.Error); | ||
return false; | ||
}; | ||
|
||
if (amount.Amount == null || moduleRef.Ref == null || name.ContractName == null || param.Parameter == null) | ||
{ | ||
var msg = $"Amount, ModuleRef, ContractName or Parameter were null, but did not produce an error"; | ||
output = (null, msg); | ||
return false; | ||
} | ||
|
||
output = (new InitContract(amount.Amount.Value, moduleRef.Ref, name.ContractName, param.Parameter), null); | ||
return true; | ||
} | ||
|
||
/// <summary> | ||
/// Copies the "init_contract" transaction in the binary format expected by the node to a byte array. | ||
/// </summary> | ||
public override byte[] ToBytes() | ||
{ | ||
using var memoryStream = new MemoryStream((int)this.Size().Size); | ||
memoryStream.WriteByte(TransactionType); | ||
memoryStream.Write(this.Amount.ToBytes()); | ||
memoryStream.Write(this.ModuleRef.ToBytes()); | ||
memoryStream.Write(this.ContractName.ToBytes()); | ||
memoryStream.Write(this.Parameter.ToBytes()); | ||
return memoryStream.ToArray(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.