Skip to content
This repository was archived by the owner on Aug 12, 2024. It is now read-only.

Commit fc7b47a

Browse files
authored
Merge pull request #225 from aeternity/release/4.2.0
Release/4.2.0
2 parents 73efa34 + b4f6132 commit fc7b47a

21 files changed

+900
-113
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ venv
22
.idea
33
**/*.pyc
44
.pytest_cache
5-
/epoch/
65
.vscode
76
aepp_sdk.egg-info
87
dist
@@ -11,3 +10,6 @@ coverage.xml
1110
docker-compose.override.yml
1211
test-results.xml
1312
.coverage
13+
private
14+
.DS_Store
15+
.envrc

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,38 @@
11
# Changelog
2+
23
All notable changes to this project will be documented in this file.
34

45
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
56
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
67

8+
9+
## [4.2.0](https://github.com/aeternity/aepp-sdk-python/releases/tag/4.2.0) ([compare](https://github.com/aeternity/aepp-sdk-python/compare/4.0.0...4.2.0)) - 2019-08-15
10+
11+
### Bug Fixes
12+
- fix: improve error handling ([3e96065](https://github.com/aeternity/aepp-sdk-python/commit/3e960652380571284e9a0ca5a1810719f0069573)).
13+
- fix: contract bytecode decoding function call in main ([0fa8fd3](https://github.com/aeternity/aepp-sdk-python/commit/0fa8fd385d65cc29206ad9aeaa6712b03210c8ce)).
14+
15+
### Code Refactoring
16+
- refactor: Imporove vm/abi detection ([bb3fcf1](https://github.com/aeternity/aepp-sdk-python/commit/bb3fcf14318ac930b0ff46f96ede7579bcd6661c)).
17+
- refactor: improve consistency of api calls ([62472f5](https://github.com/aeternity/aepp-sdk-python/commit/62472f57a4aebcb18d9cfc3dbb6a1f0bb791000b)).
18+
- refactor: improve reliability of the hashing submodule ([777f0a7](https://github.com/aeternity/aepp-sdk-python/commit/777f0a7c80090baf09357a84ae44cc5796e41c06)).
19+
- refactor: move vm and abi retrieval to the node subm ([9b2488b](https://github.com/aeternity/aepp-sdk-python/commit/9b2488b09667055e57699fecc7166e849edc8198)).
20+
- refactor: remove legacy unused code ([872cc02](https://github.com/aeternity/aepp-sdk-python/commit/872cc02e875539bdfc28c59160f074c0cc428530)).
21+
22+
### Features
23+
- feat(contract): add ability to unpack a compiled contract ([39d9f44](https://github.com/aeternity/aepp-sdk-python/commit/39d9f4414d844eb4d9ba32bf93bfe2256a98c761)).
24+
- feat(ga): add support to generalized accounts ([9e8c02c](https://github.com/aeternity/aepp-sdk-python/commit/9e8c02c8e96aaa326ebf01fb861cab61ede7c527)).
25+
- feat(ga): detect ga account when signing from a node client ([ae542b2](https://github.com/aeternity/aepp-sdk-python/commit/ae542b2285f4888e10b55601291bf0a7398114b5)).
26+
- feat(state-channels): added settle method support ([efd2ced](https://github.com/aeternity/aepp-sdk-python/commit/efd2cedd2e22036b399dd2427376a6fbd4d42308)).
27+
- feat(state-channels): state channels ga awareness ([42c233f](https://github.com/aeternity/aepp-sdk-python/commit/42c233fa84b73509ed1854a49716361d382d7436)).
28+
- feat: add function to decode a compiled/encoded contract object ([59afdaf](https://github.com/aeternity/aepp-sdk-python/commit/59afdafa526333092a3c3f3739a619cd72d14a94)).
29+
- feat: add compiler v3.2.0 support ([65759dc](https://github.com/aeternity/aepp-sdk-python/commit/65759dca1538045c44540b975adff38fb1c94c7f)).
30+
31+
### Docs
32+
33+
- docs: add documentation for ga in the docs folder
34+
35+
736
## [4.1.0](https://github.com/aeternity/aepp-sdk-python/releases/tag/4.1.0) ([compare](https://github.com/aeternity/aepp-sdk-python/compare/4.0.0...4.1.0))
837

938
### Features

Makefile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ build: build-dist
1212

1313
build-dist:
1414
@echo build
15-
python setup.py sdist
16-
python setup.py bdist_wheel
15+
poetry build
1716
@echo done
1817

1918
test: test-all

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
[![Build Status](https://ci.aepps.com/buildStatus/icon?job=aepp-sdk-python/develop)](https://ci.aepps.com/job/aepp-sdk-python/job/develop/)
55
[![PyPI version](https://badge.fury.io/py/aepp-sdk.svg)](https://badge.fury.io/py/aepp-sdk)
66

7-
## Introduction
7+
## Introduction
88

99
[This repo](https://github.com/aeternity/aepp-sdk-python) is for developing apps for the æternity blockchain in Python. Please see the [main dev site](https://dev.aepps.com) for instructions on accessing the testnet, and for running a local æternity node.
1010

aeternity/__main__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from aeternity.identifiers import NETWORK_ID_MAINNET
1414
from . import utils, signing, aens, defaults, exceptions
1515
from aeternity.contract import CompilerClient
16-
1716
from datetime import datetime, timezone
1817

1918

@@ -833,6 +832,9 @@ def inspect(obj, height, force, wait, json_):
833832
elif obj.startswith("tx_"):
834833
v = _node_cli().verify(obj)
835834
_print_object(v, title="tx")
835+
elif obj.startswith("cb_"):
836+
v = CompilerClient.decode_bytecode(obj)
837+
_print_object(v, title="contract")
836838
elif obj.isdigit() and int(obj) >= 0:
837839
v = _node_cli().get_key_block_by_height(height=int(obj))
838840
_print_object(v, title="block")

aeternity/channel.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def __init__(self, **kwargs):
5959
Applicable only for initiator (default: timeout_accept value)
6060
:param timeout_awaiting_open (int): The time frame the initiator has to start an outgoing noise session to the responder's node.
6161
Applicable only for responder (default: timeout_idle's value)
62-
:param sign (function): Function which verifies and signs transactions
62+
:param sign (TxSigner): Instance of TxSigner
6363
:param offchain_message_handler (function): Callback method to receive off-chain messages.
6464
If not provided, all the incoming messages will be ignored.
6565
:param error_handler (function): Callback method to receive error messages.
@@ -112,7 +112,7 @@ async def __message_handler(self):
112112
if msg['method'] == 'channels.message' and ChannelState.MESSAGE in self.handlers:
113113
self.handlers[ChannelState.MESSAGE](msg)
114114
if msg['method'].startswith('channels.sign'):
115-
tx = msg['params']['data']['tx']
115+
tx = msg['params']['data']['signed_tx']
116116
if msg['method'] == f'channels.sign.{self.params.role}_sign':
117117
self.__sign_channel_tx(f'channels.{self.params.role}_sign', tx)
118118
else:
@@ -182,11 +182,11 @@ def __sign_channel_tx(self, method, tx):
182182
"""
183183
Sign the transactions received over channel by the provided sign method
184184
"""
185-
signedTx = self.sign(tx)
185+
encoded_tx = self.sign.cosign_encode_transaction(tx).tx
186186
self.__enqueue_action({
187187
'method': method,
188188
'params': {
189-
'tx': signedTx.tx
189+
'signed_tx': encoded_tx
190190
}
191191
})
192192

aeternity/contract.py

Lines changed: 51 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
from aeternity import exceptions, __compiler_compatibility__
22
from aeternity import utils, defaults, hashing, openapi, identifiers
3-
import requests
43
import namedtupled
5-
from requests import ConnectionError
64

75

86
class CompilerError(exceptions.AException):
@@ -21,20 +19,6 @@ def __init__(self, compiler_url='http://localhost:3080'):
2119
self.compiler_url = compiler_url
2220
self.compiler_cli = openapi.OpenAPICli(compiler_url, compatibility_version_range=__compiler_compatibility__)
2321

24-
def _post(self, path, body, _response_object_name="CompilerReply"):
25-
"""
26-
Execute the post request to the compiler
27-
"""
28-
http_reply = None
29-
try:
30-
http_reply = requests.post(f'{self.compiler_url}{path}', json=body)
31-
object_reply = namedtupled.map(http_reply.json(), _nt_name=_response_object_name)
32-
if http_reply.status_code == 200:
33-
return object_reply
34-
raise CompilerError(f"Error: {http_reply.desc}", code=http_reply.status_code)
35-
except ConnectionError as e:
36-
raise Exception(f"Connection error to the compiler at {self.compiler_url}", e)
37-
3822
def compile(self, source_code, compiler_options={}):
3923
body = dict(
4024
code=source_code,
@@ -83,6 +67,52 @@ def decode_calldata_with_sourcecode(self, sourcecode, function, encoded_calldata
8367
}
8468
return self.compiler_cli.decode_calldata_source(body=body)
8569

70+
@staticmethod
71+
def decode_bytecode(compiled):
72+
"""
73+
Decode an encoded contract to it's components
74+
:param compiled: the encoded bytecode to decode as got from the 'compile' function
75+
:return: a named tuple with a decoded contract
76+
"""
77+
if isinstance(compiled, str):
78+
if not utils.prefix_match(identifiers.BYTECODE, compiled):
79+
raise ValueError(f"Invalid input, expecting {identifiers.BYTECODE}_ prefix")
80+
# unpack the transaction
81+
raw_contract = hashing.decode_rlp(compiled)
82+
elif isinstance(compiled, bytes):
83+
# unpack the transaction
84+
raw_contract = hashing.decode_rlp(compiled)
85+
else:
86+
raise ValueError(f"Invalid input type")
87+
88+
if not isinstance(raw_contract, list) or len(raw_contract) < 6:
89+
raise ValueError(f"Invalid contract structure")
90+
91+
# print(raw_contract)
92+
tag = hashing._int_decode(raw_contract[0])
93+
vsn = hashing._int_decode(raw_contract[1])
94+
if tag != identifiers.OBJECT_TAG_SOPHIA_BYTE_CODE:
95+
raise ValueError(f"Invalid input, expecting object type {identifiers.OBJECT_TAG_SOPHIA_BYTE_CODE}, got {tag}")
96+
# this is the hash
97+
contract_data = dict(
98+
raw=raw_contract,
99+
tag=tag,
100+
vsn=vsn,
101+
src_hash=raw_contract[2],
102+
type_info=[],
103+
bytecode=raw_contract[4],
104+
compiler_version=hashing._binary_decode(raw_contract[5], str),
105+
)
106+
# print(type_info)
107+
for t in raw_contract[3]:
108+
contract_data["type_info"].append(dict(
109+
fun_hash=t[0],
110+
fun_name=hashing._binary_decode(t[1], str),
111+
arg_type=t[2],
112+
out_type=t[3],
113+
))
114+
return namedtupled.map(contract_data, _nt_name="ContractBin")
115+
86116

87117
class ContractError(Exception):
88118
pass
@@ -118,7 +148,6 @@ def call(self, contract_id,
118148
gas=defaults.CONTRACT_GAS,
119149
gas_price=defaults.CONTRACT_GAS_PRICE,
120150
fee=defaults.FEE,
121-
vm_version=None,
122151
abi_version=None,
123152
tx_ttl=defaults.TX_TTL):
124153
"""Call a sophia contract"""
@@ -128,13 +157,12 @@ def call(self, contract_id,
128157
# check if the contract exists
129158
try:
130159
self.client.get_contract(pubkey=contract_id)
131-
except exceptions.OpenAPIClientException:
160+
except openapi.OpenAPIClientException:
132161
raise ContractError(f"Contract {contract_id} not found")
133162

134163
try:
135164
# retrieve the correct vm/abi version
136-
vm, abi = self._get_vm_abi_versions()
137-
vm_version = vm if vm_version is None else vm_version
165+
_, abi = self.client.get_vm_abi_versions()
138166
abi_version = abi if abi_version is None else abi_version
139167
# get the transaction builder
140168
txb = self.client.tx_builder
@@ -149,7 +177,7 @@ def call(self, contract_id,
149177
# post the transaction to the chain
150178
self.client.broadcast_transaction(tx_signed.tx, tx_signed.hash)
151179
return tx_signed
152-
except exceptions.OpenAPIClientException as e:
180+
except openapi.OpenAPIClientException as e:
153181
raise ContractError(e)
154182

155183
def get_call_object(self, tx_hash):
@@ -182,7 +210,7 @@ def create(self,
182210
"""
183211
try:
184212
# retrieve the correct vm/abi version
185-
vm, abi = self._get_vm_abi_versions()
213+
vm, abi = self.client.get_vm_abi_versions()
186214
vm_version = vm if vm_version is None else vm_version
187215
abi_version = abi if abi_version is None else abi_version
188216
# get the transaction builder
@@ -193,7 +221,7 @@ def create(self,
193221
tx = txb.tx_contract_create(account.get_address(), bytecode, init_calldata,
194222
amount, deposit, gas, gas_price, vm_version, abi_version,
195223
fee, ttl, nonce)
196-
# store the contract address in the instance variabl
224+
# store the contract address in the instance variable
197225
self.address = hashing.contract_id(account.get_address(), nonce)
198226
# sign the transaction
199227
tx_signed = self.client.sign_transaction(account, tx, metadata={"contract_id": self.address})
@@ -202,16 +230,3 @@ def create(self,
202230
return tx_signed
203231
except openapi.OpenAPIClientException as e:
204232
raise ContractError(e)
205-
206-
def _get_vm_abi_versions(self):
207-
"""
208-
Check the version of the node and retrieve the correct values for abi and vm version
209-
"""
210-
protocol_version = self.client.get_consensus_protocol_version()
211-
if protocol_version == identifiers.PROTOCOL_ROMA:
212-
return identifiers.CONTRACT_ROMA_VM, identifiers.CONTRACT_ROMA_ABI
213-
if protocol_version == identifiers.PROTOCOL_MINERVA:
214-
return identifiers.CONTRACT_MINERVA_VM, identifiers.CONTRACT_MINERVA_ABI
215-
if protocol_version == identifiers.PROTOCOL_FORTUNA:
216-
return identifiers.CONTRACT_FORTUNA_VM, identifiers.CONTRACT_FORTUNA_ABI
217-
raise exceptions.UnsupportedNodeVersion(f"Version {self.client.api_version} is not supported")

aeternity/defaults.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
TX_TTL = 0
1717
FEE = 0
1818
# contracts
19-
CONTRACT_GAS = 1000000000
19+
CONTRACT_GAS = 10000
2020
CONTRACT_GAS_PRICE = 1000000000
2121
CONTRACT_DEPOSIT = 0
2222
CONTRACT_AMOUNT = 0
@@ -44,3 +44,7 @@
4444
# channels
4545
CHANNEL_ENDPOINT = 'channel'
4646
CHANNEL_URL = 'ws://127.0.0.1:3014'
47+
# Generalized accounts
48+
GA_AUTH_FUNCTION = "authorize"
49+
GA_MAX_AUTH_FUN_GAS = 50000
50+
GA_ACCOUNTS_NONCE = 0 # for tx in ga transactions the nonce must be 0

aeternity/hashing.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from nacl.hash import blake2b
99
from nacl.encoding import RawEncoder
1010

11-
from aeternity import identifiers, utils
11+
from aeternity import identifiers
1212

1313

1414
def _base58_encode(data):
@@ -70,16 +70,18 @@ def encode(prefix: str, data) -> str:
7070

7171
def decode(data):
7272
"""
73-
Decode data using the default encoding/decoding algorithm
73+
Decode data using the default encoding/decoding algorithm.
74+
It will raise ValueError if the input data is not recognized and cannot be decoded
7475
:param data: a encoded and prefixed string (ex tx_..., sg_..., ak_....)
7576
:return: the raw bytes
7677
"""
77-
78-
if data is None or len(data.strip()) < 3 or data[2] != '_':
78+
if data is None or len(data.strip()) <= 3 or data[2] != '_':
7979
raise ValueError('Invalid hash')
8080
if data[0:2] in identifiers.IDENTIFIERS_B64:
8181
return _base64_decode(data[3:])
82-
return _base58_decode(data[3:])
82+
if data[0:2] in identifiers.IDENTIFIERS_B58:
83+
return _base58_decode(data[3:])
84+
raise ValueError(f"Unrecognized prefix {data[0:2]}")
8385

8486

8587
def encode_rlp(prefix, data):
@@ -176,7 +178,10 @@ def _binary(val):
176178
s = int(math.ceil(val.bit_length() / 8))
177179
return val.to_bytes(s, 'big')
178180
if isinstance(val, str):
179-
return val.encode("utf-8")
181+
try:
182+
return decode(val)
183+
except ValueError:
184+
return val.encode("utf-8")
180185
if isinstance(val, bytes):
181186
return val
182187
raise ValueError("Byte serialization not supported")
@@ -194,9 +199,11 @@ def _binary_decode(data, data_type=None):
194199

195200

196201
def _id(id_str):
197-
"""Utility function to create and _id type"""
198-
if not utils.is_valid_hash(id_str):
199-
raise ValueError(f"Unrecognized entity {id_str}")
202+
"""
203+
Utility function to create an _id type
204+
"""
205+
# if not utils.is_valid_hash(id_str):
206+
# raise ValueError(f"Unrecognized entity {id_str}")
200207
id_tag = identifiers.ID_PREFIX_TO_TAG.get(id_str[0:2])
201208
if id_tag is None:
202209
raise ValueError(f"Unrecognized prefix {id_str[0:2]}")

0 commit comments

Comments
 (0)