From ea3d7bace27dfb39172918a8d875cf30b292209e Mon Sep 17 00:00:00 2001 From: CristhianRodriguezMolina <50219561+CristhianRodriguezMolina@users.noreply.github.com> Date: Tue, 9 Aug 2022 15:04:07 -0500 Subject: [PATCH 1/3] Add optional sequence number for preconditions (#213) --- lib/tx_build/optional_sequence_number.ex | 33 ++++++ lib/tx_build/preconditions.ex | 24 +++-- .../optional_sequence_number_test.exs | 36 +++++++ test/tx_build/preconditions_test.exs | 101 +++++++++++++++++- 4 files changed, 182 insertions(+), 12 deletions(-) create mode 100644 lib/tx_build/optional_sequence_number.ex create mode 100644 test/tx_build/optional_sequence_number_test.exs diff --git a/lib/tx_build/optional_sequence_number.ex b/lib/tx_build/optional_sequence_number.ex new file mode 100644 index 00000000..1e8af6b5 --- /dev/null +++ b/lib/tx_build/optional_sequence_number.ex @@ -0,0 +1,33 @@ +defmodule Stellar.TxBuild.OptionalSequenceNumber do + @moduledoc """ + `OptionalSequenceNumber` struct definition. + """ + alias StellarBase.XDR.OptionalSequenceNumber + alias Stellar.TxBuild.SequenceNumber + + @behaviour Stellar.TxBuild.XDR + + @type sequence_number :: SequenceNumber.t() | nil + + @type t :: %__MODULE__{sequence_number: sequence_number()} + + defstruct [:sequence_number] + + @impl true + def new(sequence_number \\ nil, opts \\ []) + + def new(%SequenceNumber{} = sequence_number, _opts) do + %__MODULE__{sequence_number: sequence_number} + end + + def new(nil, _opts), do: %__MODULE__{sequence_number: nil} + + @impl true + def to_xdr(%__MODULE__{sequence_number: nil}), do: OptionalSequenceNumber.new() + + def to_xdr(%__MODULE__{sequence_number: sequence_number}) do + sequence_number + |> SequenceNumber.to_xdr() + |> OptionalSequenceNumber.new() + end +end diff --git a/lib/tx_build/preconditions.ex b/lib/tx_build/preconditions.ex index 76bde01f..421a9d18 100644 --- a/lib/tx_build/preconditions.ex +++ b/lib/tx_build/preconditions.ex @@ -6,7 +6,8 @@ defmodule Stellar.TxBuild.Preconditions do TimeBounds, LedgerBounds, SequenceNumber, - SignerKey + SignerKey, + OptionalSequenceNumber } alias StellarBase.XDR.TimeBounds, as: TimeBoundsXDR @@ -16,7 +17,6 @@ defmodule Stellar.TxBuild.Preconditions do PreconditionsV2, PreconditionType, Duration, - OptionalSequenceNumber, SignerKeyList, TimePoint, UInt32, @@ -28,7 +28,7 @@ defmodule Stellar.TxBuild.Preconditions do @type precond_v2 :: [ time_bounds: TimeBounds.t(), ledger_bounds: LedgerBounds.t(), - min_seq_num: SequenceNumber.t(), + min_seq_num: OptionalSequenceNumber.t(), min_seq_age: non_neg_integer(), min_seq_ledger_gap: non_neg_integer(), extra_signers: list(SignerKey.t()) @@ -63,7 +63,7 @@ defmodule Stellar.TxBuild.Preconditions do with {:ok, time_bounds} <- validate_time_bounds(time_bounds), {:ok, ledger_bounds} <- validate_ledger_bounds(ledger_bounds), - {:ok, min_seq_num} <- validate_min_seq_num(min_seq_num), + {:ok, min_seq_num} <- validate_optional_min_seq_num(min_seq_num), {:ok, min_seq_age} <- validate_min_seq_age(min_seq_age), {:ok, min_seq_ledger_gap} <- validate_min_seq_ledger_gap(min_seq_ledger_gap), {:ok, extra_signers} <- validate_extra_signers([], extra_signers) do @@ -117,7 +117,7 @@ defmodule Stellar.TxBuild.Preconditions do time_bounds = TimeBounds.to_xdr(time_bounds) ledger_bounds = LedgerBounds.to_xdr(ledger_bounds) - min_seq_num = min_seq_num |> SequenceNumber.to_xdr() |> OptionalSequenceNumber.new() + min_seq_num = OptionalSequenceNumber.to_xdr(min_seq_num) min_seq_age = Duration.new(min_seq_age) min_seq_ledger_gap = UInt32.new(min_seq_ledger_gap) extra_signers_list = get_extra_signers_list(extra_signers) @@ -150,11 +150,17 @@ defmodule Stellar.TxBuild.Preconditions do defp validate_ledger_bounds(_ledger_bounds), do: {:error, :invalid_ledger_bounds} - @spec validate_min_seq_num(min_seq_num :: SequenceNumber.t()) :: validation() - defp validate_min_seq_num(%SequenceNumber{} = min_seq_num), - do: {:ok, min_seq_num} + @spec validate_optional_min_seq_num(min_seq_num :: SequenceNumber.t() | nil) :: validation() + defp validate_optional_min_seq_num(nil), + do: {:ok, OptionalSequenceNumber.new()} - defp validate_min_seq_num(_min_seq_num), do: {:error, :invalid_min_seq_num} + defp validate_optional_min_seq_num(%SequenceNumber{} = min_seq_num), + do: + min_seq_num + |> OptionalSequenceNumber.new() + |> (&{:ok, &1}).() + + defp validate_optional_min_seq_num(_min_seq_num), do: {:error, :invalid_min_seq_num} @spec validate_min_seq_age(min_seq_age :: non_neg_integer()) :: validation() defp validate_min_seq_age(min_seq_age) diff --git a/test/tx_build/optional_sequence_number_test.exs b/test/tx_build/optional_sequence_number_test.exs new file mode 100644 index 00000000..bd103dde --- /dev/null +++ b/test/tx_build/optional_sequence_number_test.exs @@ -0,0 +1,36 @@ +defmodule Stellar.TxBuild.OptionalSequenceNumberTest do + use ExUnit.Case + + alias Stellar.TxBuild.{SequenceNumber, OptionalSequenceNumber} + alias StellarBase.XDR.SequenceNumber, as: SequenceNumberXDR + alias StellarBase.XDR.OptionalSequenceNumber, as: OptionalSequenceNumberXDR + + setup do + %{sequence_number: SequenceNumber.new(4_130_487_228_432_385)} + end + + test "new/2", %{sequence_number: sequence_number} do + %OptionalSequenceNumber{sequence_number: ^sequence_number} = + OptionalSequenceNumber.new(sequence_number) + end + + test "new/2 without_sequence_number" do + %OptionalSequenceNumber{sequence_number: nil} = OptionalSequenceNumber.new() + end + + test "to_xdr/1", %{sequence_number: sequence_number} do + %OptionalSequenceNumberXDR{ + sequence_number: %SequenceNumberXDR{sequence_number: 4_130_487_228_432_385} + } = + sequence_number + |> OptionalSequenceNumber.new() + |> OptionalSequenceNumber.to_xdr() + end + + test "to_xdr/1 without_sequence_number" do + %OptionalSequenceNumberXDR{sequence_number: nil} = + nil + |> OptionalSequenceNumber.new() + |> OptionalSequenceNumber.to_xdr() + end +end diff --git a/test/tx_build/preconditions_test.exs b/test/tx_build/preconditions_test.exs index daa481ea..69fec314 100644 --- a/test/tx_build/preconditions_test.exs +++ b/test/tx_build/preconditions_test.exs @@ -1,7 +1,14 @@ defmodule Stellar.TxBuild.PreconditionsTest do use ExUnit.Case - alias Stellar.TxBuild.{Preconditions, TimeBounds, LedgerBounds, SequenceNumber, SignerKey} + alias Stellar.TxBuild.{ + Preconditions, + TimeBounds, + LedgerBounds, + SequenceNumber, + SignerKey, + OptionalSequenceNumber + } alias StellarBase.XDR.{ PreconditionsV2, @@ -171,12 +178,14 @@ defmodule Stellar.TxBuild.PreconditionsTest do extra_signers: extra_signers, extra_signers_sdk: extra_signers_sdk } do + optional_min_seq_num = OptionalSequenceNumber.new(min_seq_num) + %Preconditions{ type: :precond_v2, preconditions: [ time_bounds: ^time_bounds, ledger_bounds: ^ledger_bounds, - min_seq_num: ^min_seq_num, + min_seq_num: ^optional_min_seq_num, min_seq_age: ^min_seq_age, min_seq_ledger_gap: ^min_seq_ledger_gap, extra_signers: ^extra_signers_sdk @@ -191,6 +200,36 @@ defmodule Stellar.TxBuild.PreconditionsTest do extra_signers: extra_signers ) end + + test "Precondition type precond_v2 with min_seq_num nil", %{ + time_bounds: time_bounds, + ledger_bounds: ledger_bounds, + min_seq_age: min_seq_age, + min_seq_ledger_gap: min_seq_ledger_gap, + extra_signers: extra_signers, + extra_signers_sdk: extra_signers_sdk + } do + optional_min_seq_num = OptionalSequenceNumber.new() + + %Preconditions{ + type: :precond_v2, + preconditions: [ + time_bounds: ^time_bounds, + ledger_bounds: ^ledger_bounds, + min_seq_num: ^optional_min_seq_num, + min_seq_age: ^min_seq_age, + min_seq_ledger_gap: ^min_seq_ledger_gap, + extra_signers: ^extra_signers_sdk + ] + } = + Preconditions.new( + time_bounds: time_bounds, + ledger_bounds: ledger_bounds, + min_seq_age: min_seq_age, + min_seq_ledger_gap: min_seq_ledger_gap, + extra_signers: extra_signers + ) + end end describe "to_xdr/1" do @@ -223,6 +262,8 @@ defmodule Stellar.TxBuild.PreconditionsTest do min_seq_ledger_gap: min_seq_ledger_gap, extra_signers_sdk: extra_signers_sdk } do + optional_min_seq_num = OptionalSequenceNumber.new(min_seq_num) + %PreconditionsXDR{ type: %PreconditionType{identifier: :PRECOND_V2}, preconditions: %PreconditionsV2{ @@ -262,7 +303,61 @@ defmodule Stellar.TxBuild.PreconditionsTest do preconditions: [ time_bounds: time_bounds, ledger_bounds: ledger_bounds, - min_seq_num: min_seq_num, + min_seq_num: optional_min_seq_num, + min_seq_age: min_seq_age, + min_seq_ledger_gap: min_seq_ledger_gap, + extra_signers: extra_signers_sdk + ] + }) + end + + test "Preconditions type precond_v2 with min_seq_num nil", %{ + time_bounds: time_bounds, + ledger_bounds: ledger_bounds, + min_seq_age: min_seq_age, + min_seq_ledger_gap: min_seq_ledger_gap, + extra_signers_sdk: extra_signers_sdk + } do + optional_min_seq_num = OptionalSequenceNumber.new() + + %PreconditionsXDR{ + type: %PreconditionType{identifier: :PRECOND_V2}, + preconditions: %PreconditionsV2{ + time_bounds: %OptionalTimeBounds{time_bounds: nil}, + ledger_bounds: %OptionalLedgerBounds{ledger_bounds: nil}, + min_seq_num: %OptionalSequenceNumberXDR{sequence_number: nil}, + min_seq_age: %Duration{value: 1500}, + min_seq_ledger_gap: %UInt32{datum: 2500}, + extra_signers: %SignerKeyList{ + signer_keys: [ + %SignerKeyXDR{ + signer_key: %UInt256{ + datum: + <<111, 94, 211, 67, 247, 211, 243, 210, 220, 210, 84, 19, 15, 150, 110, 92, + 222, 73, 207, 189, 198, 89, 226, 31, 8, 156, 110, 212, 64, 137, 133, 22>> + }, + type: %SignerKeyType{ + identifier: :SIGNER_KEY_TYPE_ED25519 + } + }, + %StellarBase.XDR.SignerKey{ + signer_key: %StellarBase.XDR.UInt256{ + datum: + <<166, 253, 99, 166, 207, 231, 51, 29, 108, 218, 82, 161, 161, 241, 223, 129, + 248, 20, 182, 229, 112, 154, 211, 208, 111, 24, 212, 20, 168, 1, 184, 145>> + }, + type: %StellarBase.XDR.SignerKeyType{identifier: :SIGNER_KEY_TYPE_HASH_X} + } + ] + } + } + } = + Preconditions.to_xdr(%Preconditions{ + type: :precond_v2, + preconditions: [ + time_bounds: time_bounds, + ledger_bounds: ledger_bounds, + min_seq_num: optional_min_seq_num, min_seq_age: min_seq_age, min_seq_ledger_gap: min_seq_ledger_gap, extra_signers: extra_signers_sdk From 09544385f5cf4aefcdfa1f4d2e42fd071f70b520 Mon Sep 17 00:00:00 2001 From: CristhianRodriguezMolina <50219561+CristhianRodriguezMolina@users.noreply.github.com> Date: Tue, 9 Aug 2022 15:39:20 -0500 Subject: [PATCH 2/3] Add protocol 19 documentation (#211) --- README.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1506b2f1..4713eb00 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,12 @@ The **Stellar SDK** enables the construction, signing and encoding of Stellar [t This library is aimed at developers building Elixir applications that interact with the [**Stellar network**][stellar]. +#### Protocol Version Support +| Protocol | Version | +| --------- | ------------ | +| 18 | >= v0.8 | +| 19 | >= v0.9 | + ## Documentation The **Stellar SDK** is composed of two complementary components: **`TxBuild`** + **`Horizon`**. * **`TxBuild`** - used for [**building transactions.**](#building-transactions) @@ -102,7 +108,7 @@ source_account = Stellar.TxBuild.Account.new("GDC3W2X5KUTZRTQIKXM5D2I5WG5JYSEJQW Stellar.TxBuild.new(source_account) # initialize a transaction with options -# allowed options: memo, sequence_number, base_fee, time_bounds +# allowed options: memo, sequence_number, base_fee, preconditions Stellar.TxBuild.new(source_account, memo: memo, sequence_number: sequence_number) ``` @@ -169,13 +175,27 @@ base_fee = Stellar.TxBuild.BaseFee.new(1_000) |> Stellar.TxBuild.set_base_fee(base_fee) ``` -#### Time bounds -[TimeBounds][stellar-docs-time-bounds] are optional UNIX timestamps (in seconds), determined by ledger time. A lower and upper bound of when this transaction will be valid. +#### Preconditions +Proposed on the [Stellar protocol-19][stellar-protocol-19] implementation, [Preconditions][stellar-docs-preconditions] are conditions that determines if a transaction is valid or not, and implements the following conditions: + +- **Time bounds**: [TimeBounds][stellar-docs-time-bounds] are optional UNIX timestamps (in seconds), determined by ledger time. A lower and upper bound of when this transaction will be valid. + +- **Ledger bounds**: [LedgerBounds][stellar-docs-ledger-bounds] are like Time Bounds, except they apply to ledger numbers. With them set, a transaction will only be valid for ledger numbers that fall into the range you set. + +- **Minimum Sequence Number**: [Minimum sequence number][stellar-docs-min-seq-num] if is set, the transaction will only be valid when `S` (the minimum sequence number) satisfies `minSeqNum <= S < tx.seqNum`. If is not set, the default behavior applies (the transaction’s sequence number must be exactly one greater than the account’s sequence number) + +- **Minimum Sequence Age**: [Minimum sequence age][stellar-docs-min-seq-age] is based on the account's [sequence number age][stellar-docs-account-seq-num-age]. When is set, the transaction is only valid after a particular duration (in seconds) elapses since the account’s sequence number age. + +- **Minimum Sequence Ledger Gap**: [Minimum sequence ledger gap][stellar-docs-min-seq-ledger-gap] is based on the account's [sequence number age][stellar-docs-account-seq-num-age], this is similar to the minimum sequence age, except it’s expressed as a number of ledgers rather than a duration of time. + +- **Extra Signers**: A transaction can specify up to two [Extra signers][stellar-docs-extra-signers] (of any type) even if those signatures would not otherwise be required to authorize the transaction. ```elixir # set the source account (this accound should exist in the ledger) source_account = Stellar.TxBuild.Account.new("GDC3W2X5KUTZRTQIKXM5D2I5WG5JYSEJQWEELVPQ5YMWZR6CA2JJ35RW") +# TimeBounds --------------------------------------------------------------------------- + # no time bounds for a transaction time_bounds = Stellar.TxBuild.TimeBounds.new(:none) @@ -194,11 +214,43 @@ time_bounds = Stellar.TxBuild.TimeBounds.new( # timeout time_bounds = Stellar.TxBuild.TimeBounds.set_timeout(1_643_990_815) -# set the time bounds for the transaction +# LedgerBounds ------------------------------------------------------------------------- + +# no ledger bounds for a transaction +ledger_bounds = Stellar.TxBuild.LedgerBounds.new(:none) + +# ledger bounds with ledger numbers +ledger_bounds = Stellar.TxBuild.LedgerBounds.new(min_ledger: 0, max_ledger: 1_234_567_890) + +# SequenceNumber ----------------------------------------------------------------------- + +# minimum sequence number for a transaction with value 0 +min_seq_num = Stellar.TxBuild.SequenceNumber.new() + +# minimum sequence number +min_seq_num = Stellar.TxBuild.SequenceNumber.new(1_000_000) + +# ExtraSigners + +extra_signers = ["GA2YG3YULNTUEMMLN4HUQVL7B37GJTYSRZYH6HZUFLXFDCCGKLXIXMDT"] + +# Preconditions ------------------------------------------------------------------------ + +preconditions = + Stellar.TxBuild.Preconditions.new( + time_bounds: time_bounds, + ledger_bounds: ledger_bounds, + min_seq_num: min_seq_num, + min_seq_age: 30, + min_seq_ledger_gap: 5, + extra_signers: extra_signers + ) + +# set the preconditions for the transaction {:ok, tx_build} = source_account |> Stellar.TxBuild.new() - |> Stellar.TxBuild.set_time_bounds(time_bounds) + |> Stellar.TxBuild.set_preconditions(preconditions) ``` #### Operations @@ -785,8 +837,16 @@ Made with 💙 by [kommitters Open Source](https://kommit.co) [stellar-docs-sequence-number]: https://developers.stellar.org/docs/glossary/transactions/#sequence-number [stellar-docs-fee]: https://developers.stellar.org/docs/glossary/transactions/#fee [stellar-docs-memo]: https://developers.stellar.org/docs/glossary/transactions/#memo +[stellar-docs-preconditions]: https://developers.stellar.org/docs/glossary/transactions/#validity-conditions [stellar-docs-time-bounds]: https://developers.stellar.org/docs/glossary/transactions/#time-bounds +[stellar-docs-ledger-bounds]: https://developers.stellar.org/docs/glossary/transactions/#ledger-bounds +[stellar-docs-min-seq-num]: https://developers.stellar.org/docs/glossary/transactions/#minimum-sequence-number +[stellar-docs-min-seq-age]: https://developers.stellar.org/docs/glossary/transactions/#minimum-sequence-age +[stellar-docs-min-seq-ledger-gap]: https://developers.stellar.org/docs/glossary/transactions/#minimum-sequence-ledger-gap +[stellar-docs-extra-signers]: https://developers.stellar.org/docs/glossary/transactions/#extra-signers [stellar-docs-tx-envelope]: https://developers.stellar.org/docs/glossary/transactions/#transaction-envelopes [stellar-docs-list-operations]: https://developers.stellar.org/docs/start/list-of-operations [stellar-docs-tx-signatures]: https://developers.stellar.org/docs/glossary/multisig/#transaction-signatures [stellar-cap-27]: https://stellar.org/protocol/cap-27 +[stellar-protocol-19]: https://stellar.org/blog/announcing-protocol-19 +[stellar-docs-account-seq-num-age]: https://developers.stellar.org/docs/glossary/accounts/#sequence-time-and-ledger From 134ba9609acd8aba76a06649d07734c50de9be09 Mon Sep 17 00:00:00 2001 From: CristhianRodriguezMolina <50219561+CristhianRodriguezMolina@users.noreply.github.com> Date: Tue, 9 Aug 2022 15:48:55 -0500 Subject: [PATCH 3/3] Prepare to release v0.9.2 (#214) --- CHANGELOG.md | 4 ++++ README.md | 2 +- mix.exs | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20f2fc66..7faead61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 0.9.2 (09.08.2022) +* Fix preconditions to allow minimum sequence number field to be optional. +* Add stellar protocol 19 documentation. + ## 0.9.1 (08.08.2022) * Add scorecards actions diff --git a/README.md b/README.md index 4713eb00..1575b5ad 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ The **Stellar SDK** is composed of two complementary components: **`TxBuild`** + ```elixir def deps do [ - {:stellar_sdk, "~> 0.9.1"} + {:stellar_sdk, "~> 0.9.2"} ] end ``` diff --git a/mix.exs b/mix.exs index 1f8d1bc7..a47e01d0 100644 --- a/mix.exs +++ b/mix.exs @@ -2,7 +2,7 @@ defmodule Stellar.MixProject do use Mix.Project @github_url "https://github.com/kommitters/stellar_sdk" - @version "0.9.1" + @version "0.9.2" def project do [