parent
2135d1050c
commit
a3295b9650
@ -1,219 +1,294 @@ |
||||
from enum import ( |
||||
Enum, |
||||
auto |
||||
) |
||||
from enum import Enum, auto |
||||
|
||||
from rlp.sedes import ( |
||||
big_endian_int, |
||||
Binary, |
||||
CountableList, |
||||
List, |
||||
Text |
||||
) |
||||
from rlp.sedes import big_endian_int, Binary, CountableList, List, Text |
||||
|
||||
from eth_rlp import ( |
||||
HashableRLP |
||||
) |
||||
from eth_rlp import HashableRLP |
||||
|
||||
from eth_utils.curried import ( |
||||
to_int, |
||||
hexstr_if_str, |
||||
) |
||||
|
||||
|
||||
class StakingSettings: |
||||
PRECISION = 18 |
||||
MAX_DECIMAL = 1000000000000000000 |
||||
|
||||
class Directive(Enum): # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L120 |
||||
|
||||
class Directive( |
||||
Enum |
||||
): # https://github.com/harmony-one/sdk/blob/99a827782fabcd5f91f025af0d8de228956d42b4/packages/harmony-staking/src/stakingTransaction.ts#L120 |
||||
def _generate_next_value_(name, start, count, last_values): |
||||
return count |
||||
|
||||
CreateValidator = auto() |
||||
EditValidator = auto() |
||||
Delegate = auto() |
||||
Undelegate = auto() |
||||
CollectRewards = auto() |
||||
|
||||
|
||||
FORMATTERS = { |
||||
'directive': hexstr_if_str(to_int), # delegatorAddress is already formatted before the call |
||||
'nonce': hexstr_if_str(to_int), |
||||
'gasPrice': hexstr_if_str(to_int), |
||||
'gasLimit': hexstr_if_str(to_int), |
||||
'chainId': hexstr_if_str(to_int), |
||||
"directive": hexstr_if_str( |
||||
to_int |
||||
), # delegatorAddress is already formatted before the call |
||||
"nonce": hexstr_if_str(to_int), |
||||
"gasPrice": hexstr_if_str(to_int), |
||||
"gasLimit": hexstr_if_str(to_int), |
||||
"chainId": hexstr_if_str(to_int), |
||||
} |
||||
|
||||
|
||||
class CollectRewards: |
||||
@staticmethod |
||||
def UnsignedChainId(): |
||||
class UnsignedChainId(HashableRLP): |
||||
fields = ( |
||||
('directive', big_endian_int), |
||||
('stakeMsg', CountableList(Binary.fixed_length(20, allow_empty=True))), |
||||
('nonce', big_endian_int), |
||||
('gasPrice', big_endian_int), |
||||
('gasLimit', big_endian_int), |
||||
('chainId', big_endian_int), |
||||
("directive", big_endian_int), |
||||
("stakeMsg", CountableList(Binary.fixed_length(20, allow_empty=True))), |
||||
("nonce", big_endian_int), |
||||
("gasPrice", big_endian_int), |
||||
("gasLimit", big_endian_int), |
||||
("chainId", big_endian_int), |
||||
) |
||||
|
||||
return UnsignedChainId |
||||
|
||||
@staticmethod |
||||
def SignedChainId(): |
||||
class SignedChainId(HashableRLP): |
||||
fields = CollectRewards.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId |
||||
fields = CollectRewards.UnsignedChainId()._meta.fields[ |
||||
:-1 |
||||
] + ( # drop chainId |
||||
("v", big_endian_int), |
||||
("r", big_endian_int), |
||||
("s", big_endian_int), |
||||
) |
||||
|
||||
return SignedChainId |
||||
|
||||
@staticmethod |
||||
def Unsigned(): |
||||
class Unsigned(HashableRLP): |
||||
fields = CollectRewards.UnsignedChainId()._meta.fields[:-1] # drop chainId |
||||
|
||||
return Unsigned |
||||
|
||||
@staticmethod |
||||
def Signed(): |
||||
class Signed(HashableRLP): |
||||
fields = CollectRewards.Unsigned()._meta.fields[:-3] + ( # drop last 3 for raw.pop() |
||||
fields = CollectRewards.Unsigned()._meta.fields[ |
||||
:-3 |
||||
] + ( # drop last 3 for raw.pop() |
||||
("v", big_endian_int), |
||||
("r", big_endian_int), |
||||
("s", big_endian_int), |
||||
) |
||||
|
||||
return Signed |
||||
|
||||
|
||||
class DelegateOrUndelegate: |
||||
@staticmethod |
||||
def UnsignedChainId(): |
||||
class UnsignedChainId(HashableRLP): |
||||
fields = ( |
||||
('directive', big_endian_int), |
||||
('stakeMsg', List([Binary.fixed_length(20, allow_empty=True),Binary.fixed_length(20, allow_empty=True),big_endian_int],True)), |
||||
('nonce', big_endian_int), |
||||
('gasPrice', big_endian_int), |
||||
('gasLimit', big_endian_int), |
||||
('chainId', big_endian_int), |
||||
("directive", big_endian_int), |
||||
( |
||||
"stakeMsg", |
||||
List( |
||||
[ |
||||
Binary.fixed_length(20, allow_empty=True), |
||||
Binary.fixed_length(20, allow_empty=True), |
||||
big_endian_int, |
||||
], |
||||
True, |
||||
), |
||||
), |
||||
("nonce", big_endian_int), |
||||
("gasPrice", big_endian_int), |
||||
("gasLimit", big_endian_int), |
||||
("chainId", big_endian_int), |
||||
) |
||||
|
||||
return UnsignedChainId |
||||
|
||||
@staticmethod |
||||
def SignedChainId(): |
||||
class SignedChainId(HashableRLP): |
||||
fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId |
||||
fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[ |
||||
:-1 |
||||
] + ( # drop chainId |
||||
("v", big_endian_int), |
||||
("r", big_endian_int), |
||||
("s", big_endian_int), |
||||
) |
||||
|
||||
return SignedChainId |
||||
|
||||
@staticmethod |
||||
def Unsigned(): |
||||
class Unsigned(HashableRLP): |
||||
fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[:-1] # drop chainId |
||||
fields = DelegateOrUndelegate.UnsignedChainId()._meta.fields[ |
||||
:-1 |
||||
] # drop chainId |
||||
|
||||
return Unsigned |
||||
|
||||
@staticmethod |
||||
def Signed(): |
||||
class Signed(HashableRLP): |
||||
fields = DelegateOrUndelegate.Unsigned()._meta.fields[:-3] + ( # drop last 3 for raw.pop() |
||||
fields = DelegateOrUndelegate.Unsigned()._meta.fields[ |
||||
:-3 |
||||
] + ( # drop last 3 for raw.pop() |
||||
("v", big_endian_int), |
||||
("r", big_endian_int), |
||||
("s", big_endian_int), |
||||
) |
||||
|
||||
return Signed |
||||
|
||||
|
||||
class CreateValidator: |
||||
@staticmethod |
||||
def UnsignedChainId(): |
||||
class UnsignedChainId(HashableRLP): |
||||
fields = ( |
||||
('directive', big_endian_int), |
||||
('stakeMsg', List([ # list with the following members |
||||
Binary.fixed_length(20, allow_empty=True), # validatorAddress |
||||
List([Text()]*5,True), # description is Text of 5 elements |
||||
List([List([big_endian_int],True)]*3,True), # commission rate is made up of 3 integers in an array [ [int1], [int2], [int3] ] |
||||
big_endian_int, # min self delegation |
||||
big_endian_int, # max total delegation |
||||
CountableList(Binary.fixed_length(48, allow_empty=True)), # bls-public-keys array of unspecified length, each key of 48 |
||||
CountableList(Binary.fixed_length(96, allow_empty=True)), # bls-key-sigs array of unspecified length, each sig of 96 |
||||
big_endian_int, # amount |
||||
], True)), # strictly these number of elements |
||||
('nonce', big_endian_int), |
||||
('gasPrice', big_endian_int), |
||||
('gasLimit', big_endian_int), |
||||
('chainId', big_endian_int), |
||||
("directive", big_endian_int), |
||||
( |
||||
"stakeMsg", |
||||
List( |
||||
[ # list with the following members |
||||
Binary.fixed_length( |
||||
20, allow_empty=True |
||||
), # validatorAddress |
||||
List( |
||||
[Text()] * 5, True |
||||
), # description is Text of 5 elements |
||||
List( |
||||
[List([big_endian_int], True)] * 3, True |
||||
), # commission rate is made up of 3 integers in an array [ [int1], [int2], [int3] ] |
||||
big_endian_int, # min self delegation |
||||
big_endian_int, # max total delegation |
||||
CountableList( |
||||
Binary.fixed_length(48, allow_empty=True) |
||||
), # bls-public-keys array of unspecified length, each key of 48 |
||||
CountableList( |
||||
Binary.fixed_length(96, allow_empty=True) |
||||
), # bls-key-sigs array of unspecified length, each sig of 96 |
||||
big_endian_int, # amount |
||||
], |
||||
True, |
||||
), |
||||
), # strictly these number of elements |
||||
("nonce", big_endian_int), |
||||
("gasPrice", big_endian_int), |
||||
("gasLimit", big_endian_int), |
||||
("chainId", big_endian_int), |
||||
) |
||||
|
||||
return UnsignedChainId |
||||
|
||||
@staticmethod |
||||
def SignedChainId(): |
||||
class SignedChainId(HashableRLP): |
||||
fields = CreateValidator.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId |
||||
fields = CreateValidator.UnsignedChainId()._meta.fields[ |
||||
:-1 |
||||
] + ( # drop chainId |
||||
("v", big_endian_int), |
||||
("r", big_endian_int), |
||||
("s", big_endian_int), |
||||
) |
||||
|
||||
return SignedChainId |
||||
|
||||
@staticmethod |
||||
def Unsigned(): |
||||
class Unsigned(HashableRLP): |
||||
fields = CreateValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId |
||||
|
||||
return Unsigned |
||||
|
||||
@staticmethod |
||||
def Signed(): |
||||
class Signed(HashableRLP): |
||||
fields = CreateValidator.Unsigned()._meta.fields[:-3] + ( # drop last 3 for raw.pop() |
||||
fields = CreateValidator.Unsigned()._meta.fields[ |
||||
:-3 |
||||
] + ( # drop last 3 for raw.pop() |
||||
("v", big_endian_int), |
||||
("r", big_endian_int), |
||||
("s", big_endian_int), |
||||
) |
||||
|
||||
return Signed |
||||
|
||||
|
||||
class EditValidator: |
||||
@staticmethod |
||||
def UnsignedChainId(): |
||||
class UnsignedChainId(HashableRLP): |
||||
fields = ( |
||||
('directive', big_endian_int), |
||||
('stakeMsg', List([ # list with the following members |
||||
Binary.fixed_length(20, allow_empty=True), # validatorAddress |
||||
List([Text()]*5,True), # description is Text of 5 elements |
||||
List([big_endian_int],True), # new rate is in a list |
||||
big_endian_int, # min self delegation |
||||
big_endian_int, # max total delegation |
||||
Binary.fixed_length(48, allow_empty=True), # slot key to remove |
||||
Binary.fixed_length(48, allow_empty=True), # slot key to add |
||||
], True)), # strictly these number of elements |
||||
('nonce', big_endian_int), |
||||
('gasPrice', big_endian_int), |
||||
('gasLimit', big_endian_int), |
||||
('chainId', big_endian_int), |
||||
("directive", big_endian_int), |
||||
( |
||||
"stakeMsg", |
||||
List( |
||||
[ # list with the following members |
||||
Binary.fixed_length( |
||||
20, allow_empty=True |
||||
), # validatorAddress |
||||
List( |
||||
[Text()] * 5, True |
||||
), # description is Text of 5 elements |
||||
List([big_endian_int], True), # new rate is in a list |
||||
big_endian_int, # min self delegation |
||||
big_endian_int, # max total delegation |
||||
Binary.fixed_length( |
||||
48, allow_empty=True |
||||
), # slot key to remove |
||||
Binary.fixed_length( |
||||
48, allow_empty=True |
||||
), # slot key to add |
||||
], |
||||
True, |
||||
), |
||||
), # strictly these number of elements |
||||
("nonce", big_endian_int), |
||||
("gasPrice", big_endian_int), |
||||
("gasLimit", big_endian_int), |
||||
("chainId", big_endian_int), |
||||
) |
||||
|
||||
return UnsignedChainId |
||||
|
||||
@staticmethod |
||||
def SignedChainId(): |
||||
class SignedChainId(HashableRLP): |
||||
fields = EditValidator.UnsignedChainId()._meta.fields[:-1] + ( # drop chainId |
||||
fields = EditValidator.UnsignedChainId()._meta.fields[ |
||||
:-1 |
||||
] + ( # drop chainId |
||||
("v", big_endian_int), |
||||
("r", big_endian_int), |
||||
("s", big_endian_int), |
||||
) |
||||
|
||||
return SignedChainId |
||||
|
||||
@staticmethod |
||||
def Unsigned(): |
||||
class Unsigned(HashableRLP): |
||||
fields = EditValidator.UnsignedChainId()._meta.fields[:-1] # drop chainId |
||||
|
||||
return Unsigned |
||||
|
||||
@staticmethod |
||||
def Signed(): |
||||
class Signed(HashableRLP): |
||||
fields = EditValidator.Unsigned()._meta.fields[:-3] + ( # drop last 3 for raw.pop() |
||||
fields = EditValidator.Unsigned()._meta.fields[ |
||||
:-3 |
||||
] + ( # drop last 3 for raw.pop() |
||||
("v", big_endian_int), |
||||
("r", big_endian_int), |
||||
("s", big_endian_int), |
||||
) |
||||
|
||||
return Signed |
||||
|
@ -1,10 +1,9 @@ |
||||
from pyhmy.bech32 import ( |
||||
bech32 |
||||
) |
||||
from pyhmy.bech32 import bech32 |
||||
|
||||
|
||||
def test_encode(): |
||||
bech32.encode('one', 5, [121, 161]) |
||||
bech32.encode("one", 5, [121, 161]) |
||||
|
||||
def test_decode(): |
||||
bech32.decode('one', 'one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9') |
||||
|
||||
def test_decode(): |
||||
bech32.decode("one", "one1a0x3d6xpmr6f8wsyaxd9v36pytvp48zckswvv9") |
||||
|
@ -1,71 +1,80 @@ |
||||
import pytest |
||||
|
||||
from pyhmy import ( |
||||
contract |
||||
) |
||||
from pyhmy import contract |
||||
|
||||
from pyhmy.rpc import ( |
||||
exceptions |
||||
) |
||||
from pyhmy.rpc import exceptions |
||||
|
||||
explorer_endpoint = 'http://localhost:9599' |
||||
contract_tx_hash = '0xa605852dd2fa39ed42e101c17aaca9d344d352ba9b24b14b9af94ec9cb58b31f' |
||||
explorer_endpoint = "http://localhost:9599" |
||||
contract_tx_hash = "0xa605852dd2fa39ed42e101c17aaca9d344d352ba9b24b14b9af94ec9cb58b31f" |
||||
# deployedBytecode from json file |
||||
contract_code = '0x6080604052348015600f57600080fd5b506004361060285760003560e01c80634936cd3614602d575b600080fd5b604080516001815290519081900360200190f3fea2646970667358221220fa3fa0e8d0267831a59f4dd5edf39a513d07e98461cb06660ad28d4beda744cd64736f6c634300080f0033' |
||||
contract_code = "0x6080604052348015600f57600080fd5b506004361060285760003560e01c80634936cd3614602d575b600080fd5b604080516001815290519081900360200190f3fea2646970667358221220fa3fa0e8d0267831a59f4dd5edf39a513d07e98461cb06660ad28d4beda744cd64736f6c634300080f0033" |
||||
contract_address = None |
||||
fake_shard = 'http://example.com' |
||||
fake_shard = "http://example.com" |
||||
|
||||
|
||||
def _test_contract_rpc(fn, *args, **kwargs): |
||||
if not callable(fn): |
||||
pytest.fail(f'Invalid function: {fn}') |
||||
pytest.fail(f"Invalid function: {fn}") |
||||
|
||||
try: |
||||
response = fn(*args, **kwargs) |
||||
except Exception as e: |
||||
if isinstance(e, exceptions.RPCError) and 'does not exist/is not available' in str(e): |
||||
pytest.skip(f'{str(e)}') |
||||
elif isinstance(e, exceptions.RPCError) and 'estimateGas returned' in str(e): |
||||
pytest.skip(f'{str(e)}') |
||||
pytest.fail(f'Unexpected error: {e.__class__} {e}') |
||||
if isinstance( |
||||
e, exceptions.RPCError |
||||
) and "does not exist/is not available" in str(e): |
||||
pytest.skip(f"{str(e)}") |
||||
elif isinstance(e, exceptions.RPCError) and "estimateGas returned" in str(e): |
||||
pytest.skip(f"{str(e)}") |
||||
pytest.fail(f"Unexpected error: {e.__class__} {e}") |
||||
return response |
||||
|
||||
|
||||
def test_get_contract_address_from_hash(setup_blockchain): |
||||
global contract_address |
||||
contract_address = _test_contract_rpc(contract.get_contract_address_from_hash, contract_tx_hash) |
||||
contract_address = _test_contract_rpc( |
||||
contract.get_contract_address_from_hash, contract_tx_hash |
||||
) |
||||
assert isinstance(contract_address, str) |
||||
|
||||
|
||||
def test_call(setup_blockchain): |
||||
if not contract_address: |
||||
pytest.skip('Contract address not loaded yet') |
||||
called = _test_contract_rpc(contract.call, contract_address, 'latest') |
||||
assert isinstance(called, str) and called.startswith('0x') |
||||
pytest.skip("Contract address not loaded yet") |
||||
called = _test_contract_rpc(contract.call, contract_address, "latest") |
||||
assert isinstance(called, str) and called.startswith("0x") |
||||
|
||||
|
||||
def test_estimate_gas(setup_blockchain): |
||||
if not contract_address: |
||||
pytest.skip('Contract address not loaded yet') |
||||
pytest.skip("Contract address not loaded yet") |
||||
gas = _test_contract_rpc(contract.estimate_gas, contract_address) |
||||
assert isinstance(gas, int) |
||||
|
||||
|
||||
def test_get_code(setup_blockchain): |
||||
if not contract_address: |
||||
pytest.skip('Contract address not loaded yet') |
||||
code = _test_contract_rpc(contract.get_code, contract_address, 'latest') |
||||
pytest.skip("Contract address not loaded yet") |
||||
code = _test_contract_rpc(contract.get_code, contract_address, "latest") |
||||
assert code == contract_code |
||||
|
||||
|
||||
def test_get_storage_at(setup_blockchain): |
||||
if not contract_address: |
||||
pytest.skip('Contract address not loaded yet') |
||||
storage = _test_contract_rpc(contract.get_storage_at, contract_address, '0x0', 'latest') |
||||
assert isinstance(storage, str) and storage.startswith('0x') |
||||
pytest.skip("Contract address not loaded yet") |
||||
storage = _test_contract_rpc( |
||||
contract.get_storage_at, contract_address, "0x0", "latest" |
||||
) |
||||
assert isinstance(storage, str) and storage.startswith("0x") |
||||
|
||||
|
||||
def test_errors(): |
||||
with pytest.raises(exceptions.RPCError): |
||||
contract.get_contract_address_from_hash('', fake_shard) |
||||
contract.get_contract_address_from_hash("", fake_shard) |
||||
with pytest.raises(exceptions.RPCError): |
||||
contract.call('', '', endpoint=fake_shard) |
||||
contract.call("", "", endpoint=fake_shard) |
||||
with pytest.raises(exceptions.RPCError): |
||||
contract.estimate_gas('', endpoint=fake_shard) |
||||
contract.estimate_gas("", endpoint=fake_shard) |
||||
with pytest.raises(exceptions.RPCError): |
||||
contract.get_code('', 'latest', endpoint=fake_shard) |
||||
contract.get_code("", "latest", endpoint=fake_shard) |
||||
with pytest.raises(exceptions.RPCError): |
||||
contract.get_storage_at('', 1, 'latest', endpoint=fake_shard) |
||||
contract.get_storage_at("", 1, "latest", endpoint=fake_shard) |
||||
|
Loading…
Reference in new issue