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