Skip to content

Commit

Permalink
Merge pull request #1 from txbody-org/init-address
Browse files Browse the repository at this point in the history
Address Cbor Encoder & Decoder
  • Loading branch information
piyushthapa authored Aug 1, 2024
2 parents e3df9a9 + 59094cf commit 37f3e47
Show file tree
Hide file tree
Showing 6 changed files with 690 additions and 1 deletion.
94 changes: 94 additions & 0 deletions .github/workflows/elixir.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Elixir CI

# Define workflow that runs when changes are pushed to the
# `main` branch or pushed to a PR branch that targets the `main`
# branch. Change the branch name if your project uses a
# different name for the main branch like "master" or "production".
on:
push:
branches: ["main"] # adapt branch for project
pull_request:
branches: ["main"] # adapt branch for project

# Sets the ENV `MIX_ENV` to `test` for running tests
env:
MIX_ENV: test

permissions:
contents: read

jobs:
test:
# Set up a Postgres DB service. By default, Phoenix applications
# use Postgres. This creates a database for running tests.
# Additional services can be defined here if required.

runs-on: ubuntu-latest
name: Test on OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}}
strategy:
# Specify the OTP and Elixir versions to use when building
# and running the workflow steps.
matrix:
otp: ["25.0.4"] # Define the OTP version [required]
elixir: ["1.15.0"] # Define the elixir version [required]
steps:
# Step: Setup Elixir + Erlang image as the base.
- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
otp-version: ${{matrix.otp}}
elixir-version: ${{matrix.elixir}}

# Step: Check out the code.
- name: Checkout code
uses: actions/checkout@v3

# Step: Define how to cache deps. Restores existing cache if present.
- name: Cache deps
id: cache-deps
uses: actions/cache@v3
env:
cache-name: cache-elixir-deps
with:
path: deps
key: ${{ runner.os }}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-mix-${{ env.cache-name }}-
# Step: Define how to cache the `_build` directory. After the first run,
# this speeds up tests runs a lot. This includes not re-compiling our
# project's downloaded deps every run.
- name: Cache compiled build
id: cache-build
uses: actions/cache@v3
env:
cache-name: cache-compiled-build
with:
path: _build
key: ${{ runner.os }}-mix-${{ env.cache-name }}-${{ hashFiles('**/mix.lock') }}
restore-keys: |
${{ runner.os }}-mix-${{ env.cache-name }}-
${{ runner.os }}-mix-
# Step: Download project dependencies. If unchanged, uses
# the cached version.
- name: Install dependencies
run: mix deps.get

# Step: Compile the project treating any warnings as errors.
# Customize this step if a different behavior is desired.
- name: Compiles without warnings
run: mix compile --warnings-as-errors

# Step: Check that the checked in code has already been formatted.
# This step fails if something was found unformatted.
# Customize this step as desired.
- name: Check Formatting
run: mix format --check-formatted

- name: Check for code consistency
run: mix credo --strict

# Step: Execute the tests.
- name: Run tests
run: mix test
93 changes: 93 additions & 0 deletions lib/sutra/cardano/address.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
defmodule Sutra.Cardano.Address do
@moduledoc """
Cardano Address
"""

use TypedStruct

alias __MODULE__, as: Address
alias Sutra.Cardano.Address.Parser

@type credential_type :: :vkey | :script
@type address_type :: :shelley | :reward | :byron
@type stake_credential :: Credential.t() | Pointer.t() | nil
@type network :: :mainnet | :testnet

typedstruct module: Credential do
@moduledoc """
Address Credential
"""
field(:credential_type, Address.credential_type(), enforce: true)
field(:hash, String.t(), enforce: true)
end

typedstruct module: Pointer do
@moduledoc """
Address Pointer
"""
field(:slot, Integer.t(), enforce: true)
field(:tx_index, Integer.t(), enforce: true)
field(:cert_index, Integer.t(), enforce: true)
end

typedstruct do
field(:network, Address.network(), enforce: true)
field(:address_type, address_type(), enforce: true)
field(:payment_credential, Credential.t())
field(:stake_credential, stake_credential())
end

@doc """
Return an address from a Bech32
## Example
iex> from_bech32("addr1vx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzers66hrl8")
{:ok, %Address{network: :mainnet, address_type: :shelley, ..}}
iex> from_bech32("stake178phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtcccycj5")
{:ok, %Address{network: :mainnet, address_type: :reward, ..}}
"""
def from_bech32(bech32_addr) do
case Bech32.decode(bech32_addr) do
{:ok, _hmr, bytes} -> Parser.decode(bytes)
_ -> {:error, "Invalid Bech32 address"}
end
end

@doc """
Convert an address to a Bech32
## Example
iex> to_bech32(%Address{network: :mainnet, address_type: :shelley})
{:ok, "addr1vx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzers66hrl8"}
iex> to_bech32(%Address{network: :mainnet, address_type: :reward})
{:ok, "stake178phkx6acpnf78fuvxn0mkew3l0fd058hzquvz7w36x4gtcccycj5"}
"""
def to_bech32(%Address{} = address) do
hrp_prefix =
case address.address_type do
:shelley -> "addr"
:reward -> "stake"
_ -> ""
end

is_padded =
case address.stake_credential do
%Pointer{} -> false
_ -> true
end

hrp =
if address.network == :mainnet do
hrp_prefix
else
hrp_prefix <> "_test"
end

data = Parser.encode(address) |> Bech32.convertbits(8, 5, is_padded)
Bech32.encode_from_5bit(hrp, data)
end
end
Loading

0 comments on commit 37f3e47

Please sign in to comment.