Skip to content
This repository has been archived by the owner on Nov 15, 2021. It is now read-only.

Commit

Permalink
Added support for StateTransaction and StateDescriptors (#193)
Browse files Browse the repository at this point in the history
  • Loading branch information
localhuman authored and metachris committed Jan 24, 2018
1 parent 12a0136 commit 2200802
Show file tree
Hide file tree
Showing 12 changed files with 317 additions and 27 deletions.
153 changes: 153 additions & 0 deletions neo/Core/State/StateDescriptor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
from neocore.IO.BinaryReader import BinaryReader
from neocore.IO.BinaryWriter import BinaryWriter
from neo.Core.Mixins import SerializableMixin
from neo.IO.MemoryStream import StreamManager
from neocore.Fixed8 import Fixed8

from enum import Enum


class StateType(Enum):
Account = 0x40
Validator = 0x48


class StateDescriptor(SerializableMixin):

Type = None
Key = None
Field = None
Value = None

@property
def SystemFee(self):
if self.Type == StateType.Account:
return Fixed8.Zero()
elif self.Type == StateType.Validator:
return self.GetSystemFee_Validator()

def Size(self):
"""
Get the total size in bytes of the object.
Returns:
int: size.
"""
return super(StateDescriptor, self).Size()

def Deserialize(self, reader: BinaryReader):
"""
Deserialize full object.
Args:
reader (neocore.IO.BinaryReader):
"""

self.Type = StateType(reader.ReadByte())

self.Key = reader.ReadVarBytes(max=100)
self.Field = reader.ReadVarString(max=32).decode('utf-8')
self.Value = reader.ReadVarBytes(max=65535)

if self.Type == StateType.Account:
self.CheckAccountState()
elif self.Type == StateType.Validator:
self.CheckValidatorState()

@staticmethod
def DeserializeFromDB(buffer):
"""
Deserialize full object.
Args:
buffer (bytes, bytearray, BytesIO): (Optional) data to create the stream from.
Returns:
ValidatorState:
"""
m = StreamManager.GetStream(buffer)
reader = BinaryReader(m)
v = StateDescriptor()
v.Deserialize(reader)

StreamManager.ReleaseStream(m)

return v

def Serialize(self, writer: BinaryWriter):
"""
Serialize full object.
Args:
writer (neo.IO.BinaryWriter):
"""
byt = None
if self.Type == StateType.Account:
byt = b'\x40'
elif self.Type == StateType.Validator:
byt = b'\x48'
writer.WriteByte(byt)
writer.WriteVarBytes(self.Key)
writer.WriteVarString(self.Field)
writer.WriteVarBytes(self.Value)

def GetSystemFee_Validator(self):

if self.Field == "Registered":
for character in self.Value:
if character != '0':
return Fixed8.FromDecimal(1000)
return Fixed8.Zero()

raise Exception("Invalid operation")

def CheckAccountState(self):
if len(self.Key) != 20:
raise Exception("Invalid Key format")
if self.Field != "Votes":
raise Exception("Invalid Field")

def CheckValidatorState(self):
if len(self.Key) != 33:
raise Exception("Invalid Validator State Key Format")
if self.Field != "Registered":
raise Exception("Invalid Field for validator")

def ToJson(self):
"""
Convert object members to a dictionary that can be parsed as JSON.
Returns:
dict:
"""

type = ''
if self.Type == StateType.Validator:
type = 'Validator'
elif self.Type == StateType.Account:
type = 'Account'

return {
'type': type,
'key': self.Key.hex(),
'field': self.Field,
'value': self.Value.hex()
}

def Verify(self):

if self.Type == StateType.Account:
return self.VerifyAccountState()
elif self.Type == StateType.Validator:
return self.VerifyValidatorState()
raise Exception("Invalid State Descriptor")

def VerifyAccountState(self):
# @TODO
# Implement VerifyAccount State
raise NotImplementedError()

def VerifyValidatorState(self):
if self.Field == 'Registered':
return True
return False
19 changes: 0 additions & 19 deletions neo/Core/State/VoteState.py

This file was deleted.

1 change: 0 additions & 1 deletion neo/Core/TX/ClaimTransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
class ClaimTransaction(Transaction):
Claims = set()

@property
def Size(self):
"""
Get the total size in bytes of the object.
Expand Down
2 changes: 1 addition & 1 deletion neo/Core/TX/EnrollmentTransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def Size(self):
Returns:
int: size.
"""
return self.Size() + sys.getsizeof(int)
return super(EnrollmentTransaction, self).Size() + sys.getsizeof(int)

def SystemFee(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion neo/Core/TX/InvocationTransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def Size(self):
Returns:
int: size.
"""
return self.Size() + sys.getsizeof(int)
return super(InvocationTransaction, self).Size() + sys.getsizeof(int)

def DeserializeExclusiveData(self, reader):
"""
Expand Down
2 changes: 1 addition & 1 deletion neo/Core/TX/MinerTransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def Size(self):
Returns:
int: size.
"""
return self.Size() + sys.getsizeof(int)
return super(MinerTransaction, self).Size() + sys.getsizeof(int)

def DeserializeExclusiveData(self, reader):
"""
Expand Down
128 changes: 128 additions & 0 deletions neo/Core/TX/StateTransaction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
from neo.Core.TX.Transaction import *
from neocore.Fixed8 import Fixed8
from neo.Core.State.StateDescriptor import StateDescriptor


class StateTransaction(Transaction):

Descriptors = None

def Size(self):
"""
Get the total size in bytes of the object.
Returns:
int: size.
"""

return super(StateTransaction, self).Size()

def __init__(self, *args, **kwargs):
"""
Create an instance.
Args:
*args:
**kwargs:
"""
super(StateTransaction, self).__init__(*args, **kwargs)

self.Type = TransactionType.StateTransaction

def NetworkFee(self):
"""
Get the network fee for a claim transaction.
Returns:
Fixed8: currently fixed to 0.
"""
return Fixed8(0)

def SystemFee(self):
amount = Fixed8.Zero()
for d in self.Descriptors:
amount += d.SystemFee
return amount

def DeserializeExclusiveData(self, reader):
"""
Deserialize full object.
Args:
reader (neo.IO.BinaryReader):
Raises:
Exception: If the transaction type is incorrect or if there are no claims.
"""
self.Descriptors = []
self.Type = TransactionType.StateTransaction

self.Descriptors = reader.ReadSerializableArray('neo.Core.State.StateDescriptor.StateDescriptor')

# num_descriptors = reader.ReadByte()
# if num_descriptors > 16:
# raise Exception("Invalid number of descriptors")
# for i in range(0, num_descriptors):
# descriptor = StateDescriptor()
# descriptor.Deserialize(reader)
# self.Descriptors.append(descriptor)

def GetScriptHashesForVerifying(self):
"""
Get a list of script hashes for verifying transactions.
Raises:
Exception: if there are no valid transactions to claim from.
Returns:
list: of UInt160 type script hashes.
"""

raise NotImplementedError()

def GetScriptHashesForVerifying_Account(self, descriptor):
raise NotImplementedError()

def GetScriptHashesForVerifying_Validator(self, descriptor):
raise NotImplementedError()

def SerializeExclusiveData(self, writer):
"""
Serialize object.
Args:
writer (neo.IO.BinaryWriter):
"""
writer.WriteSerializableArray(self.Descriptors)

def ToJson(self):
"""
Convert object members to a dictionary that can be parsed as JSON.
Returns:
dict:
"""

json = super(StateTransaction, self).ToJson()
descriptors = [d.ToJson() for d in self.Descriptors]

json['descriptors'] = descriptors

return json

def Verify(self, mempool):
"""
Verify the transaction.
Args:
mempool:
Returns:
bool: True if verified. False otherwise.
"""

for descriptor in self.Descriptors:
if not descriptor.Verify():
return False

return super(StateTransaction, self).Verify(mempool)
4 changes: 4 additions & 0 deletions neo/Core/TX/Transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class TransactionType(object):
VotingTransaction = b'\x24'
RegisterTransaction = b'\x40'
ContractTransaction = b'\x80'
StateTransaction = b'\x90'
AgencyTransaction = b'\xb0'
PublishTransaction = b'\xd0'
InvocationTransaction = b'\xd1'
Expand Down Expand Up @@ -450,6 +451,7 @@ def DeserializeFrom(reader):
from neo.Core.TX.PublishTransaction import PublishTransaction
from neo.Core.TX.InvocationTransaction import InvocationTransaction
from neo.Core.TX.EnrollmentTransaction import EnrollmentTransaction
from neo.Core.TX.StateTransaction import StateTransaction

if ttype == int.from_bytes(TransactionType.RegisterTransaction, 'little'):
tx = RegisterTransaction()
Expand All @@ -465,6 +467,8 @@ def DeserializeFrom(reader):
tx = InvocationTransaction()
elif ttype == int.from_bytes(TransactionType.EnrollmentTransaction, 'little'):
tx = EnrollmentTransaction()
elif ttype == int.from_bytes(TransactionType.StateTransaction, 'little'):
tx = StateTransaction()
else:
tx = Transaction()
tx.Type = ttype
Expand Down
22 changes: 18 additions & 4 deletions neo/Core/test_block.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
from neo.Utils.NeoTestCase import NeoTestCase
import os
import sys
from neo.Blockchain import GetGenesis
import binascii
from neo.IO.Helper import Helper
from neo.Core.Blockchain import Blockchain
from neo.Core.Block import Block
from neocore.Cryptography.Crypto import Crypto
from neocore.Cryptography.MerkleTree import MerkleTree
import json
from neocore.Fixed8 import Fixed8
from neo.Core.TX.StateTransaction import StateTransaction
from neo.Core.State.StateDescriptor import StateDescriptor, StateType


class BlocksTestCase(NeoTestCase):
Expand Down Expand Up @@ -267,3 +266,18 @@ def test_testnet797966(self):
self.assertEqual(blockfrom_trimmed.ConsensusData, block.ConsensusData)
self.assertEqual(blockfrom_trimmed.NextConsensus, block.NextConsensus)
self.assertEqual(len(block.Transactions), len(blockfrom_trimmed.Transactions))

block_bytes = b'\x00\x00\x00\x00\xed\x00.\xa6\xc6(\xa2\x80\x1a\x95E\x8f\xb4\xaa\xcc$\x98\x031\xd3\xca2!-\x1a\xaa/m!\xb8V\xa4Jj\xd4\xad6\xc1a\xb25-5\xe5\xd7\xec\xe2\x13T\xbb\xf6\x02\xa4\xaes\x98((\xd7i\n\x81\xfa\xf6\xf0neZ\\\xe6\x0f\x00\x88\xb7\xcf\xab\xb3d\x88M\xf3\x81-\xb9\x82\xf3\xb0\x08\x9a!\xa2x\x98\x8e\xfe\xecj\x02{%\x01\xfdE\x01@\xa0Z\xf8\xda\x93*\xd0\x04\xdcq\x06\xd5\x86\x07G\xd0 \\;\xb3cg\xcc\xf3=}B\x07\xf4\'\xf1k\xe4\x80|\xb9\x07\x9e\xa8\x8b\x9f\xf0\xd7 \xbb\x1a\x82\xa1\xa5\x9a\xb0\x9e\xf4\x1a\xb7\x89S\xd4\xeai\x8f\x8b\xcb_@x\x8a%\x07\x93\x14\x04\xbc\r\x9d\xdb\xc2A\xc0\x8d\x07\x13F\xd1An\xf8$\x02\xa0\xe4\n1?I\x9a\xfc.\xf0e\x8c\xda\x9dv\xc3\xac\xd7\xdav\x91:V\xba\xb2pK\xceF\xc4Z\xc6boj\xdc\x1e\xc3\xd4\x06@\xd9\x8cH\xa7\xa5*\xba\xb9\xac~\xf8\x05\xecONc\xb0\xa5\x1aH0\xfd\x11K>pL\x84\xd9\xaeK,\x87\x92x5d\xc1\xa7m\x1f!)\x9fl\xa5\x12>\x07md\x83\x17#\x1f"\xb9Uk\x82\xaf\xfb\xaa\xbb@n\x8b\x1a\x96\x85O\xf2\x84-\xeb\xa3d\xe4\xad\x9e":\xbc\xab5\xe6$P\xef\x91\xe3\xc3\xaf\x11\xc2f\xde\xf6\xec\xc55k\xe7\xdf\x9a?Z2v\xedF4\x01p)5\r\xd2t+\x10Y\xe2\x8a\x05I:\x13I@\xc3H\xb3/\x06\x08\xcb\xcf\xd1\xa5\xd8\xa6,3m1\x909\x1b\x17\xda^\xea\xee\xcc\x1e\x02\x90h\xb0\x04\x1a\x94\x92~o\xf2^\x0e\xca\x821\xb90\t\x98\xd2\xad\xc8\xdc\xb5<\xa4\x99\xc2c\xe9\xb9\x91\x7fS\xa0=\t\xf1U!\x02\t\xe7\xfdA\xdf\xb5\xc2\xf8\xdcr\xeb05\x8a\xc1\x00\xea\x8cr\xda\x18\x84{\xef\xe0n\xad\xe6\x8c\xeb\xfc\xb9!\x03\'\xda\x12\xb5\xc4\x02\x00\xe9\xf6UiGk\xbf\xf2!\x8d\xa4\xf3%H\xffC\xb68~\xc1Aj#\x1e\xe8!\x03O\xf5\xce\xea\xc4\x1a\xcf"\xcd^\xd2\xda\x17\xa6\xdfM\xd85\x8f\xcb+\xfb\x1aC \x8a\xd0\xfe\xaa\xb2tk!\x02l\xe3[)\x14z\xd0\x9eJ\xfeN\xc4\xa71\x90\x95\xf0\x81\x98\xfa\x8b\xab\xbe<V\xe9p\xb1CR\x8d"!\x03\x8d\xdd\xc0l\xe6\x87gzS\xd5O\tm%\x91\xba#\x02\x06\x8c\xf1#\xc1\xf2\xd7\\-\xdd\xc5BUy!\x03\x9d\xaf\xd8W\x1ad\x10X\xcc\xc82\xc5\xe2\x11\x1e\xa3\x9b\t\xc0\xbd\xe3`P\x91C\x84\xf7\xa4\x8b\xce\x9b\xf9!\x02\xd0+\x18s\xa0\x86<\xd0B\xccq}\xa3\x1c\xea\r|\xf9\xdb2\xb7MLr\xc0\x1b\x00\x11P>."W\xae\x02\x00\x00\x88\xb7\xcf\xab\x00\x00\x00\x00\x90\x00\x01H!\x03\xc0\x89\xd7\x12+\x84\nI5#N\x82\xe2j\xe5\xef\xd0\xc2\xac\xb6\'#\x9d\xc9\xf2\x071\x137\xb6\xf2\xc1\nRegistered\x01\x01\x00\x01\xcbA\x84\xf0\xa9nrel\x1f\xbd\xd4\xf7\\\xcaVu\x19\xe9\t\xfdC\xce\xfc\xec\x13\xd6\xc6\xab\xcb\x92\xa1\x00\x00\x01\xe7-(iy\xeel\xb1\xb7\xe6]\xfd\xdf\xb2\xe3\x84\x10\x0b\x8d\x14\x8ewX\xdeB\xe4\x16\x8bqy,`\x00\xb8\xfb\x05\x01\t\x00\x00q\xf9\xcf\x7f\x0e\xc7N\xc0\xb0\xf2\x8a\x92\xb1.\x10\x81WL\n\xf0\x01A@\x87\x80\xd7\xb3\xc0\xaa\xdcS\x98\x15=\xf5\xe2\xf1\xcf\x15\x9d\xb2\x1b\x8b\x0f4\xd3\x99M\x86T3\xf7\x9f\xaf\xacAh7\x83\xc4\x8a\xefQ\x0bgf\x0e1W\xb7\x01\xb9\xcaM\xd9\x94j8]W\x8f\xba}\xd2oHI#!\x03\xc0\x89\xd7\x12+\x84\nI5#N\x82\xe2j\xe5\xef\xd0\xc2\xac\xb6\'#\x9d\xc9\xf2\x071\x137\xb6\xf2\xc1\xac'

def test_testnet10412011(self):

block = Helper.AsSerializableWithType(self.block_bytes, 'neo.Core.Block.Block')
self.assertEqual(len(block.Transactions), 2)
state_tx = block.Transactions[1]
self.assertIsInstance(state_tx, StateTransaction)
self.assertEqual(len(state_tx.Descriptors), 1)
descriptor = state_tx.Descriptors[0]
self.assertIsInstance(descriptor, StateDescriptor)
self.assertEqual(descriptor.Type, StateType.Validator)
self.assertEqual(descriptor.SystemFee, Fixed8.FromDecimal(1000))
self.assertEqual(descriptor.Key, binascii.unhexlify(b'03c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1'))
Loading

0 comments on commit 2200802

Please sign in to comment.