aboutsummaryrefslogblamecommitdiff
path: root/shard/lib/data/signrev.ex
blob: 6360b5311eef786171a055278497d7ba66fd7f6f (plain) (tree)









































































                                                                                                     
defmodule SData.SignRev do
  @moduledoc"""
  Implement a simple signed object with a revision number.
  """


  defstruct [:term, :rev, :signed]

  @doc"""
  New SignRev with an unsigned initial state (use this if initial state is deterministic/not signed).
  """
  def new(init_term) do
    %__MODULE__{term: init_term, rev: 0, signed: nil}
  end

  @doc"""
  New SignRev with a signed initial state.
  """
  def new(init_term, pk) do
    bin = SData.term_bin {0, init_term}
    case Shard.Keys.sign(pk, bin) do
      {:ok, signed} ->
        {:ok, %__MODULE__{term: init_term, rev: 0, signed: signed}}
      err -> err
    end
  end

  @doc"""
  Get the current value of the SignRev.
  """
  def get(sr) do
    sr.term
  end

  @doc"""
  Update the SignRev with a new value and sign it with this pk
  """
  def set(sr, new_term, pk) do
    rev = sr.rev + 1
    bin = SData.term_bin {rev, new_term}
    case Shard.Keys.sign(pk, bin) do
      {:ok, signed} ->
        {:ok, %__MODULE__{term: new_term, rev: rev, signed: signed}}
      err -> err
    end
  end

  @doc"""
  Get the signed binary for the SignRev. Just send the output of this
  to a peer for him to update.
  """
  def signed(sr) do
    sr.signed
  end

  @doc"""
  Check that a signed binary is correct and merge it into the SignRev.

  Returns {true, new_sr} if an update happenned, {false, sr} otherwise.
  """
  def merge(sr, signed, pk) do
    case Shard.Keys.open(pk, signed) do
      {:ok, bin} ->
        {rev, new_term} = SData.term_unbin bin
        if rev > sr.rev do
          {true, %__MODULE__{term: new_term, rev: rev, signed: signed}}
        else
          {false, sr}
        end
      _ ->
        {false, sr}
    end
  end
end