Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/trust-ledger-characteristics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Important trust ledger characteristics

## Agnostic
Empty file added ppl/database/__init__.py
Empty file.
163 changes: 163 additions & 0 deletions ppl/database/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
from __future__ import annotations

import enum
import itertools
import uuid
from datetime import datetime
from typing import Optional, Callable, Tuple

from sqlalchemy import Column, Integer, String, ForeignKey, DATETIME, Enum
from sqlalchemy.orm import declarative_base, relationship

from ppl.database.typedecorators.guid import GUID
from ppl.database.typedecorators.json import JsonData
from ppl.types.state import StateType

Base = declarative_base()


class Entity(Base):
__tablename__ = 'entities'
id = Column(GUID, primary_key=True)
name = Column(String(64))

def __init__(self, id, name):
self.id = id
self.name = name


class Schema(Base):
__tablename__ = "ppl_schemas"
id = Column(GUID, primary_key=True)
name = Column(String(64), nullable=False)
version = Column(Integer, nullable=False)
entity_id = Column(GUID, ForeignKey('entities.id'))

def __init__(self, id, name, version):
self.id = id
self.name = name
self.version = version


class SchemaState(Base):
__tablename__ = "schema_states"
id = Column(GUID, primary_key=True)
name = Column(String(64))
schema_id = Column(GUID, ForeignKey("ppl_schemas.id"))

def __init__(self, id, name, schema_id):
self.id = id
self.name = name
self.schema_id = schema_id


class Wallet(Base):
__tablename__ = 'wallets'
next_id = itertools.count().__next__

id = Column(GUID, primary_key=True)
handle = Column(String(16))
public_key = Column(String(512))

def __init__(self, id, handle, public_key):
self.id = id if id else Wallet.next_id()
self.handle = handle
self.public_key = public_key


class StateValidity(enum.Enum):
Proposed = "P"
Active = "A"
Extinguished = "E"


class State(Base):
__tablename__ = 'states'

next_id = itertools.count().__next__

id = Column(GUID, primary_key=True)
logical_id = Column(GUID)
version = Column(Integer)
prior_id = Column(GUID, nullable=True)
creator_id = Column(GUID, ForeignKey("transactions.id"), nullable=False)
destroyer_id = Column(GUID, ForeignKey("transactions.id"), nullable=True)
created = Column(DATETIME)
state_type = Column(Enum(StateType))
validity = Column(Enum(StateValidity))
public = Column(JsonData(4000))
proof = Column(JsonData(4000))

creator = relationship("Transaction", foreign_keys=[creator_id])
destroyer = relationship("Transaction", foreign_keys=[destroyer_id])

__mapper_args__ = {
'polymorphic_on': state_type,
'polymorphic_identity': StateType.State
}

def __init__(self, id: uuid.UUID, logical_id: uuid.UUID, version: int, created: datetime,
validity: StateValidity, prior_id: Optional[uuid.UUID],
public: dict, proofs: dict):
self.id = id
self.logical_id = logical_id
self.version = version
self.created = created
self.prior_id = prior_id
self.validity = validity
self.public = public
self.proofs = proofs

def mutate(self, id: uuid.UUID, new_transaction: Transaction, created: datetime, mutator: Callable[[dict, dict], Tuple[dict, dict]]) -> State:
new_public, new_proofs = mutator(self.public, self.proofs)
self.validity = StateValidity.Extinguished
new_state = State(id, self.logical_id, self.version + 1, created, StateValidity.Active, self.id, new_public,
new_proofs)
new_transaction.destroy_state(self)
new_transaction.create_state(new_state)
return new_state


class IOU(State):
__tablename__ = 'states'

__mapper_args__ = {
'polymorphic_identity': StateType.IOU
}

def __init__(self, id: uuid.UUID, logical_id: uuid.UUID, version: int, created: datetime,
validity: StateValidity, prior_id: Optional[uuid.UUID],
public: dict, proofs: dict):
super().__init__(id, logical_id, version, created, validity, prior_id, public, proofs)


class Contract(State):
__tablename__ = 'states'

__mapper_args__ = {
'polymorphic_identity': StateType.Contract
}


class Transaction(Base):
__tablename__ = 'transactions'

id = Column(GUID, primary_key=True)


def __init__(self, id: uuid.UUID):
self.id = id

def create_state(self, state: State):
if state.creator_id is None:
state.creator_id = self.id
else:
raise Exception("State already initialised by another transaction")

def destroy_state(self, state: State):
if state.creator_id is None:
raise Exception("State not yet created")
elif state.destroyer_id is not None:
raise Exception("State has already been destroyed by another transaction")
else:
state.destroyer_id = self.id
Empty file.
41 changes: 41 additions & 0 deletions ppl/database/typedecorators/guid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from sqlalchemy.types import TypeDecorator, CHAR
from sqlalchemy.dialects.postgresql import UUID
import uuid

# Copied from https://docs.sqlalchemy.org/en/14/core/custom_types.html#backend-agnostic-guid-type

class GUID(TypeDecorator):
"""Platform-independent GUID type.

Uses PostgreSQL's UUID type, otherwise uses
CHAR(32), storing as stringified hex values.

"""
impl = CHAR
cache_ok = True

def load_dialect_impl(self, dialect):
if dialect.name == 'postgresql':
return dialect.type_descriptor(UUID())
else:
return dialect.type_descriptor(CHAR(32))

def process_bind_param(self, value, dialect):
if value is None:
return value
elif dialect.name == 'postgresql':
return str(value)
else:
if not isinstance(value, uuid.UUID):
return "%.32x" % uuid.UUID(value).int
else:
# hexstring
return "%.32x" % value.int

def process_result_value(self, value, dialect):
if value is None:
return value
else:
if not isinstance(value, uuid.UUID):
value = uuid.UUID(value)
return value
26 changes: 26 additions & 0 deletions ppl/database/typedecorators/json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from sqlalchemy import TypeDecorator, VARCHAR
import json

class JsonData(TypeDecorator):
"""Represents an immutable structure as a json-encoded string.

Usage::

JSONEncodedDict(255)

"""

impl = VARCHAR

cache_ok = True

def process_bind_param(self, value, dialect):
if value is not None:
value = json.dumps(value)

return value

def process_result_value(self, value, dialect):
if value is not None:
value = json.loads(value)
return value
56 changes: 56 additions & 0 deletions ppl/types/contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from decimal import Decimal

from ppl.types.iou import ContractType
from ppl.types.state import StateType, SerialisedState, State
from ppl.types.value_store import ValueStore, UOM
from ppl.types.wallet import Wallet


class Contract(ValueStore):
state_type = StateType.Contract

def __init__(self, state_id: int, contract_type: ContractType, own_wallet: Wallet, uom: UOM, amount: Decimal):
super().__init__(state_id, uom, amount)
self.contract_type = contract_type
self.own_wallet = own_wallet

def __str__(self):
return "Contract {}->{}:{} {}".format(self.contract_type, self.own_wallet, self.uom, self.amount)

def __eq__(self, other):
return isinstance(other, Contract) \
and self.contract_type.value == other.contract_type.value \
and self.own_wallet.id == other.own_wallet.id \
and self.uom == other.uom \
and self.amount == other.amount


def serialise(self) -> SerialisedState:
"""Convert state data into a generic serialised state for further handling in an abstract way by the platform"""
return SerialisedState(
self.state_type.value,
self.state_id,
{
"uom": self.uom.value,
"amount": self.amount,
"contract_type": self.contract_type.value,
"own_wallet_id": self.own_wallet.id,
}, {

}
)


@staticmethod
def deserialise(ecosystem: 'Ecosystem', dct: SerialisedState):
"""Reconstruct an IOU from a serialised state"""
instance = Contract(dct["state_id"],
Contract(dct["public"]["contract_type"]),
ecosystem.get_wallet_for_id(dct["public"]["own_wallet_id"]),
UOM(dct["public"]["uom"]),
Decimal(dct["public"]["amount"]))
instance.state_id = dct["state_id"]
return instance


State.deserialisers[StateType.IOU.value] = deserialise
3 changes: 3 additions & 0 deletions ppl/types/iou.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ class IOUType(Enum):
AccountDeposit = 2
Loan = 3

class ContractType(Enum):
"""A subclass of a State. These can be of multiple types"""
Escrow = 1

class IOU(ValueStore):
"""An IOU is a subclass of State which expresses a liability from a `from_wallet` to a `to_wallet` for a given
Expand Down
4 changes: 3 additions & 1 deletion ppl/types/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

class StateType(Enum):
"""Different states can be stored on the ledger. StateType disambiguates which state is being stored"""
IOU = 1
State = "STT"
IOU = "IOU"
Contract = "CON"


class SerialisedState:
Expand Down
16 changes: 0 additions & 16 deletions ppl/types/wallet.py
Original file line number Diff line number Diff line change
@@ -1,16 +0,0 @@
import itertools

from ppl.types.wallet_provider import WalletProvider
from ppl.utils.crypto import generate_keypair


class Wallet:
next = itertools.count().__next__

def __init__(self, wallet_id: int, code: str):
self.id = wallet_id
self.code = code
self.key = generate_keypair("w_{}_key".format(wallet_id))

def __str__(self):
return "W({}:{})".format(self.id, self.code)
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
git+https://github.com/meilof/pysnark
jwcrypto
sqlalchemy==1.4.22
pymysql==1.0.2
cryptography==3.4.7
# python-libsnark # required if we decide to use use libsnark

6 changes: 5 additions & 1 deletion tests/early_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,18 @@ def test_currency_serialisation(self):
serialised = json.dumps(one_lakh_issuance.serialise().to_json(), cls=DineroEncoder)
data = json.loads(serialised)
deserialised_state = deserialise_state(ecosystem, data)
log.debug("Deserialised currency object {}".format(deserialised_state))
print("Deserialised currency object {}".format(deserialised_state))
self.assertEqual(one_lakh_issuance, deserialised_state,
"Serialised currency object is not the same as deserialised currency")

created_states = [one_lakh_issuance]
transaction = Transaction(None, created_states, "RBI issues one lakh \u20b9.")
mi.record(transaction)

def test_contract_serialisation(self):
one_lakh_issuance = IOU(State.next_id(), IOUType.Currency, cb.main_wallet, cb.main_wallet, UOM.Currency_INR,
Decimal("100000.00"))


if __name__ == '__main__':
unittest.main()
Loading