cneal-thor-devkit


Namecneal-thor-devkit JSON
Version 1.0.0 PyPI version JSON
download
home_pagehttps://github.com/laalaguer/thor-devkit.py
SummarySDK to interact with VeChain Thor public blockchain.
upload_time2024-07-26 19:33:37
maintainerNone
docs_urlNone
authorcneal
requires_python>=3.6
licenseNone
keywords vechain thor blockchain sdk
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # VeChain Thor Devkit (SDK) in Python 3

Python 3 (Python 3.6+) library to assist smooth development on VeChain for developers and hobbyists.

|                          Content                          |
| --------------------------------------------------------- |
| Public key, private key, address conversion.              |
| Mnemonic Wallets.                                         |
| HD Wallet.                                                |
| Keystore.                                                 |
| Various Hashing functions.                                |
| Signing messages.                                         |
| Verify signature of messages.                             |
| Bloom filter.                                             |
| Transaction Assembling (**Multi-task Transaction, MTT**). |
| Fee Delegation Transaction (**VIP-191**).                 |
| Self-signed Certificate (**VIP-192**).                    |
| ABI decoding of "functions" and "events" in logs.         |

... and will always be updated with the **newest** features on VeChain.

# Install
```bash
pip3 install thor-devkit -U
```

***Caveat: Bip32 depends on the `ripemd160` hash library, which should be present on your system within OpenSSL.***

Type these in your terminal to see if they are available
```python
> python3
> import hashlib
> print('ripemd160' in hashlib.algorithms_available)
```

# Tutorials

### Private/Public Keys
```python
from thor_devkit import cry
from thor_devkit.cry import secp256k1

private_key = secp256k1.generate_privateKey()

public_key = secp256k1.derive_publicKey(private_key)

_address_bytes = cry.public_key_to_address(public_key)
address = '0x' + _address_bytes.hex()

print( address )
# 0x86d8cd908e43bc0076bc99e19e1a3c6221436ad0
print('is address?', cry.is_address(address))
# is address? True
print( cry.to_checksum_address(address) ) 
# 0x86d8CD908e43BC0076Bc99e19E1a3c6221436aD0
```

### Sign & Verify Signature

```python
from thor_devkit import cry
from thor_devkit.cry import secp256k1

# bytes
private_key = bytes.fromhex('7582be841ca040aa940fff6c05773129e135623e41acce3e0b8ba520dc1ae26a')
# bytes
msg_hash, _ = cry.keccak256([b'hello world'])

# Sign the message hash.
# bytes
signature = secp256k1.sign(msg_hash, private_key)

# Recover public key from given message hash and signature.
# bytes
public_key = secp256k1.recover(msg_hash, signature)
```

### Mnemonic Wallet

```python
from thor_devkit.cry import mnemonic

words = mnemonic.generate()
print(words)
# ['fashion', 'reduce', 'resource', 'ordinary', 'seek', 'kite', 'space', 'marriage', 'cube', 'detail', 'bundle', 'latin']

flag = mnemonic.validate(words)
print(flag)
# True

# Quickly get a Bip32 master seed for HD wallets. See below "HD Wallet".
seed = mnemonic.derive_seed(words)

# Quickly get a private key.
private_key = mnemonic.derive_private_key(words, 0)
```

### HD Wallet
Hierarchical Deterministic Wallets. See [bip-32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) and [bip-44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki).

```python
from thor_devkit import cry
from thor_devkit.cry import hdnode

# Construct an HD node from words. (Recommended)
words = 'ignore empty bird silly journey junior ripple have guard waste between tenant'.split(' ')

hd_node = cry.HDNode.from_mnemonic(
    words,
    init_path=hdnode.VET_EXTERNAL_PATH
) # VET wallet, you can input other string values to generate BTC/ETH/... wallets.

# Or, construct HD node from seed. (Advanced)
seed = '28bc19620b4fbb1f8892b9607f6e406fcd8226a0d6dc167ff677d122a1a64ef936101a644e6b447fd495677f68215d8522c893100d9010668614a68b3c7bb49f'

hd_node = cry.HDNode.from_seed(
    bytes.fromhex(seed),
    init_path=hdnode.VET_EXTERNAL_PATH
) # VET wallet, you can input other string values to generate BTC/ETH/... wallets.

# Access the HD node's properties.
priv = hd_node.private_key()
pub = hd_node.public_key()
addr = hd_node.address()
cc = hd_node.chain_code()

# Or, construct HD node from a given public key. (Advanced)
# Notice: This HD node cannot derive child HD node with "private key".
hd_node = cry.HDNode.from_public_key(pub, cc)

# Or, construct HD node from a given private key. (Advanced)
hd_node = cry.HDNode.from_private_key(priv, cc)

# Let it derive further child HD nodes.
for i in range(0, 3):
    print('addr:', '0x'+hd_node.derive(i).address().hex())
    print('priv:', hd_node.derive(i).private_key().hex())

# addr: 0x339fb3c438606519e2c75bbf531fb43a0f449a70
# priv: 27196338e7d0b5e7bf1be1c0327c53a244a18ef0b102976980e341500f492425
# addr: 0x5677099d06bc72f9da1113afa5e022feec424c8e
# priv: 0xcf44074ec3bf912d2a46b7c84fa6eb745652c9c74e674c3760dc7af07fc98b62
# addr: 0x86231b5cdcbfe751b9ddcd4bd981fc0a48afe921
# priv: 2ca054a50b53299ea3949f5362ee1d1cfe6252fbe30bea3651774790983e9348
```

### Keystore

```python
from thor_devkit.cry import keystore

ks = {
    "version": 3,
    "id": "f437ebb1-5b0d-4780-ae9e-8640178ffd77",
    "address": "dc6fa3ec1f3fde763f4d59230ed303f854968d26",
    "crypto":
    {
        "kdf": "scrypt",
        "kdfparams": {
            "dklen": 32,
            "salt": "b57682e5468934be81217ad5b14ca74dab2b42c2476864592c9f3b370c09460a",
            "n": 262144,
            "r": 8,
            "p": 1
        },
        "cipher": "aes-128-ctr",
        "ciphertext": "88cb876f9c0355a89cad88ee7a17a2179700bc4306eaf78fa67320efbb4c7e31",
        "cipherparams": {
            "iv": "de5c0c09c882b3f679876b22b6c5af21"
        },
        "mac": "8426e8a1e151b28f694849cb31f64cbc9ae3e278d02716cf5b61d7ddd3f6e728"
    }
}
password = b'123456'

# Decrypt
private_key = keystore.decrypt(ks, password)

# Encrypt
ks_backup = keystore.encrypt(private_key, password)
```

### Hash the Messages
```python
from thor_devkit import cry

result, length = cry.blake2b256([b'hello world'])
result2, length = cry.blake2b256([b'hello', b' world'])
# result == result2

result, length = cry.keccak256([b'hello world'])
result2, length = cry.keccak256([b'hello', b' world'])
# result == result2
```


### Bloom Filter
```python
from thor_devkit import Bloom

# Create a bloom filter that can store 100 items.
_k = Bloom.estimate_k(100)
b = Bloom(_k)

# Add an item to the bloom filter.
b.add(bytes('hello world', 'UTF-8'))

# Verify
b.test(bytes('hello world', 'UTF-8'))
# True
b.test(bytes('bye bye blue bird', 'UTF-8'))
# False
```

### Transaction

```python
from thor_devkit import cry, transaction

# See: https://docs.vechain.org/thor/learn/transaction-model.html#model
body = {
    "chainTag": int('0x4a', 16), # 0x4a/0x27/0xa4 See: https://docs.vechain.org/others/miscellaneous.html#network-identifier
    "blockRef": '0x00000000aabbccdd',
    "expiration": 32,
    "clauses": [
        {
            "to": '0x7567d83b7b8d80addcb281a71d54fc7b3364ffed',
            "value": 10000,
            "data": '0x000000606060'
        },
        {
            "to": '0x7567d83b7b8d80addcb281a71d54fc7b3364ffed',
            "value": 20000,
            "data": '0x000000606060'
        }
    ],
    "gasPriceCoef": 128,
    "gas": 21000,
    "dependsOn": None,
    "nonce": 12345678
}

# Construct an unsigned transaction.
tx = transaction.Transaction(body)

# Access its properties.
tx.get_signing_hash() == cry.blake2b256([tx.encode()])[0] # True

tx.get_signature() == None # True

tx.get_origin() == None # True

tx.get_intrinsic_gas() == 37432 # estimate the gas this tx gonna cost.

# Sign the transaction with a private key.
priv_key = bytes.fromhex('7582be841ca040aa940fff6c05773129e135623e41acce3e0b8ba520dc1ae26a')
message_hash = tx.get_signing_hash()
signature = cry.secp256k1.sign(message_hash, priv_key)

# Set the signature on the transaction.
tx.set_signature(signature)

# Tx origin?
print(tx.get_origin())
# 0xd989829d88b0ed1b06edf5c50174ecfa64f14a64

# Tx id?
print(tx.get_id())
# 0xda90eaea52980bc4bb8d40cb2ff84d78433b3b4a6e7d50b75736c5e3e77b71ec

# Tx encoded into bytes, ready to be sent out.
encoded_bytes = tx.encode()

# pretty print the encoded bytes.
print('0x' + encoded_bytes.hex())

# http POST transaction to send the encoded_bytes to VeChain...
# See the REST API details:
# testnet: https://sync-testnet.vechain.org/doc/swagger-ui/
# mainnet: https://sync-mainnet.vechain.org/doc/swagger-ui/
```

### Transaction (VIP-191)
[https://github.com/vechain/VIPs/blob/master/vips/VIP-191.md](https://github.com/vechain/VIPs/blob/master/vips/VIP-191.md)

```python
from thor_devkit import cry, transaction

delegated_body = {
    "chainTag": 1,
    "blockRef": '0x00000000aabbccdd',
    "expiration": 32,
    "clauses": [
        {
            "to": '0x7567d83b7b8d80addcb281a71d54fc7b3364ffed',
            "value": 10000,
            "data": '0x000000606060'
        },
        {
            "to": '0x7567d83b7b8d80addcb281a71d54fc7b3364ffed',
            "value": 20000,
            "data": '0x000000606060'
        }
    ],
    "gasPriceCoef": 128,
    "gas": 21000,
    "dependsOn": None,
    "nonce": 12345678,
    "reserved": {
        "features": 1
    }
}

delegated_tx = transaction.Transaction(delegated_body)

# Indicate it is a delegated Transaction using VIP-191.
assert delegated_tx.is_delegated() == True

# Sender
addr_1 = '0xf9ea4ba688d55cc7f0eae0dd62f8271b744637bf'

priv_1 = bytes.fromhex('58e444d4fe08b0f4d9d86ec42f26cf15072af3ddc29a78e33b0ceaaa292bcf6b')


# Gas Payer
addr_2 = '0x34b7538c2a7c213dd34c3ecc0098097d03a94dcb'

priv_2 = bytes.fromhex('0bfd6a863f347f4ef2cf2d09c3db7b343d84bb3e6fc8c201afee62de6381dc65')


h = delegated_tx.get_signing_hash() # Sender hash to be signed.
dh = delegated_tx.get_signing_hash(addr_1) # Gas Payer hash to be signed.

# Sender sign the hash.
# Gas payer sign the hash.
# Concat two parts to forge a legal signature.
sig = cry.secp256k1.sign(h, priv_1) + cry.secp256k1.sign(dh, priv_2)

delegated_tx.set_signature(sig)

assert delegated_tx.get_origin() == addr_1
assert delegated_tx.get_delegator() == addr_2
```

### Sign/Verify Certificate (VIP-192)
[https://github.com/vechain/VIPs/blob/master/vips/VIP-192.md](https://github.com/vechain/VIPs/blob/master/vips/VIP-192.md)

```python
from thor_devkit import cry
from thor_devkit.cry import secp256k1
from thor_devkit import certificate

# My address.
address = '0xd989829d88b0ed1b06edf5c50174ecfa64f14a64'
# My corresponding private key.
private_key = bytes.fromhex('7582be841ca040aa940fff6c05773129e135623e41acce3e0b8ba520dc1ae26a')

# My cert.
cert_dict = {
    'purpose': 'identification',
    'payload': {
        'type': 'text',
        'content': 'fyi'
    },
    'domain': 'localhost',
    'timestamp': 1545035330,
    'signer': address
}

# Construct a cert, without signature.
cert = certificate.Certificate(**cert_dict)

# Sign the cert with my private key.
sig_bytes = secp256k1.sign(
    cry.blake2b256([
        certificate.encode(cert).encode('utf-8')
    ])[0],
    private_key
)
signature = '0x' + sig_bytes.hex()

# Mount the signature onto the cert.
cert_dict['signature'] = signature

# Construct a cert, with signature.
cert2 = certificate.Certificate(**cert_dict)

# Verify, if verify failed it will throw Exceptions.
certificate.verify(cert2)
```

### ABI

Encode function name and parameters according to ABI.

```python
from thor_devkit import abi

abi_dict = {
        "constant": False,
        "inputs": [
            {
                "name": "a1",
                "type": "uint256"
            },
            {
                "name": "a2",
                "type": "string"
            }
        ],
        "name": "f1",
        "outputs": [
            {
                "name": "r1",
                "type": "address"
            },
            {
                "name": "r2",
                "type": "bytes"
            }
        ],
        "payable": False,
        "stateMutability": "nonpayable",
        "type": "function"
}

# Verify if abi_dict is in good shape.
f1 = abi.FUNCTION(abi_dict)

# Get a function instance of the abi.
f = abi.Function(f1)

# Get function selector:
selector = f.selector.hex()
selector == '27fcbb2f'

# Encode the function input parameters.
r = f.encode([1, 'foo'], to_hex=True)
r == '0x27fcbb2f000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000003666f6f0000000000000000000000000000000000000000000000000000000000'

# Decode function return result according to abi.
data = '000000000000000000000000abc000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000003666f6f0000000000000000000000000000000000000000000000000000000000'

r = f.decode(bytes.fromhex(data))
# {
#     "0": '0xabc0000000000000000000000000000000000001',
#     "1": b'666f6f',
#     "r1": '0xabc0000000000000000000000000000000000001',
#     "r2": b'666f6f'
# }
```

Decode logs according to data and topics.

```python
from thor_devkit import abi

e2 = abi.EVENT({
    "anonymous": True,
    "inputs": [
        {
            "indexed": True,
            "name": "a1",
            "type": "uint256"
        },
        {
            "indexed": False,
            "name": "a2",
            "type": "string"
        }
    ],
    "name": "E2",
    "type": "event"
})

ee = abi.Event(e2)

# data in hex format.
r = ee.decode(
    data=bytes.fromhex('00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003666f6f0000000000000000000000000000000000000000000000000000000000'),
    topics=[
        bytes.fromhex('0000000000000000000000000000000000000000000000000000000000000001')
    ]
)

# r == { "0": 1, "1": "foo", "a1": 1, "a2": "foo" }
```

# Tweak the Code

## Layout
```
.
├── LICENSE
├── README.md
├── requirements.txt
└── thor_devkit
    ├── __init__.py
    ├── abi.py
    ├── bloom.py
    ├── certificate.py
    ├── cry
    │   ├── __init__.py
    │   ├── address.py
    │   ├── blake2b.py
    │   ├── hdnode.py
    │   ├── keccak.py
    │   ├── keystore.py
    │   ├── mnemonic.py
    │   ├── secp256k1.py
    │   └── utils.py
    ├── rlp.py
    └── transaction.py
```

## Local Development
```bash
# install dependencies
make install
# test code
make test
```

## Knowledge

|     Name     | Bytes |                  Description                   |
| ------------ | ----- | ---------------------------------------------- |
| private key  | 32    | random number                                  |
| public key   | 65    | uncompressed, starts with "04"                 |
| address      | 20    | derived from public key                        |
| keccak256    | 32    | hash                                           |
| blake2b256   | 32    | hash                                           |
| message hash | 32    | hash of a message                              |
| signature    | 65    | signing result, last bit as recovery parameter |
| seed         | 64    | used to derive bip32 master key                |

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/laalaguer/thor-devkit.py",
    "name": "cneal-thor-devkit",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.6",
    "maintainer_email": null,
    "keywords": "vechain thor blockchain sdk",
    "author": "cneal",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/57/8b/46eebb09e03c97589d36b569a642ca5f454bfcb1f361f1a2221ef56385a8/cneal_thor_devkit-1.0.0.tar.gz",
    "platform": null,
    "description": "# VeChain Thor Devkit (SDK) in Python 3\n\nPython 3 (Python 3.6+) library to assist smooth development on VeChain for developers and hobbyists.\n\n|                          Content                          |\n| --------------------------------------------------------- |\n| Public key, private key, address conversion.              |\n| Mnemonic Wallets.                                         |\n| HD Wallet.                                                |\n| Keystore.                                                 |\n| Various Hashing functions.                                |\n| Signing messages.                                         |\n| Verify signature of messages.                             |\n| Bloom filter.                                             |\n| Transaction Assembling (**Multi-task Transaction, MTT**). |\n| Fee Delegation Transaction (**VIP-191**).                 |\n| Self-signed Certificate (**VIP-192**).                    |\n| ABI decoding of \"functions\" and \"events\" in logs.         |\n\n... and will always be updated with the **newest** features on VeChain.\n\n# Install\n```bash\npip3 install thor-devkit -U\n```\n\n***Caveat: Bip32 depends on the `ripemd160` hash library, which should be present on your system within OpenSSL.***\n\nType these in your terminal to see if they are available\n```python\n> python3\n> import hashlib\n> print('ripemd160' in hashlib.algorithms_available)\n```\n\n# Tutorials\n\n### Private/Public Keys\n```python\nfrom thor_devkit import cry\nfrom thor_devkit.cry import secp256k1\n\nprivate_key = secp256k1.generate_privateKey()\n\npublic_key = secp256k1.derive_publicKey(private_key)\n\n_address_bytes = cry.public_key_to_address(public_key)\naddress = '0x' + _address_bytes.hex()\n\nprint( address )\n# 0x86d8cd908e43bc0076bc99e19e1a3c6221436ad0\nprint('is address?', cry.is_address(address))\n# is address? True\nprint( cry.to_checksum_address(address) ) \n# 0x86d8CD908e43BC0076Bc99e19E1a3c6221436aD0\n```\n\n### Sign & Verify Signature\n\n```python\nfrom thor_devkit import cry\nfrom thor_devkit.cry import secp256k1\n\n# bytes\nprivate_key = bytes.fromhex('7582be841ca040aa940fff6c05773129e135623e41acce3e0b8ba520dc1ae26a')\n# bytes\nmsg_hash, _ = cry.keccak256([b'hello world'])\n\n# Sign the message hash.\n# bytes\nsignature = secp256k1.sign(msg_hash, private_key)\n\n# Recover public key from given message hash and signature.\n# bytes\npublic_key = secp256k1.recover(msg_hash, signature)\n```\n\n### Mnemonic Wallet\n\n```python\nfrom thor_devkit.cry import mnemonic\n\nwords = mnemonic.generate()\nprint(words)\n# ['fashion', 'reduce', 'resource', 'ordinary', 'seek', 'kite', 'space', 'marriage', 'cube', 'detail', 'bundle', 'latin']\n\nflag = mnemonic.validate(words)\nprint(flag)\n# True\n\n# Quickly get a Bip32 master seed for HD wallets. See below \"HD Wallet\".\nseed = mnemonic.derive_seed(words)\n\n# Quickly get a private key.\nprivate_key = mnemonic.derive_private_key(words, 0)\n```\n\n### HD Wallet\nHierarchical Deterministic Wallets. See [bip-32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) and [bip-44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki).\n\n```python\nfrom thor_devkit import cry\nfrom thor_devkit.cry import hdnode\n\n# Construct an HD node from words. (Recommended)\nwords = 'ignore empty bird silly journey junior ripple have guard waste between tenant'.split(' ')\n\nhd_node = cry.HDNode.from_mnemonic(\n    words,\n    init_path=hdnode.VET_EXTERNAL_PATH\n) # VET wallet, you can input other string values to generate BTC/ETH/... wallets.\n\n# Or, construct HD node from seed. (Advanced)\nseed = '28bc19620b4fbb1f8892b9607f6e406fcd8226a0d6dc167ff677d122a1a64ef936101a644e6b447fd495677f68215d8522c893100d9010668614a68b3c7bb49f'\n\nhd_node = cry.HDNode.from_seed(\n    bytes.fromhex(seed),\n    init_path=hdnode.VET_EXTERNAL_PATH\n) # VET wallet, you can input other string values to generate BTC/ETH/... wallets.\n\n# Access the HD node's properties.\npriv = hd_node.private_key()\npub = hd_node.public_key()\naddr = hd_node.address()\ncc = hd_node.chain_code()\n\n# Or, construct HD node from a given public key. (Advanced)\n# Notice: This HD node cannot derive child HD node with \"private key\".\nhd_node = cry.HDNode.from_public_key(pub, cc)\n\n# Or, construct HD node from a given private key. (Advanced)\nhd_node = cry.HDNode.from_private_key(priv, cc)\n\n# Let it derive further child HD nodes.\nfor i in range(0, 3):\n    print('addr:', '0x'+hd_node.derive(i).address().hex())\n    print('priv:', hd_node.derive(i).private_key().hex())\n\n# addr: 0x339fb3c438606519e2c75bbf531fb43a0f449a70\n# priv: 27196338e7d0b5e7bf1be1c0327c53a244a18ef0b102976980e341500f492425\n# addr: 0x5677099d06bc72f9da1113afa5e022feec424c8e\n# priv: 0xcf44074ec3bf912d2a46b7c84fa6eb745652c9c74e674c3760dc7af07fc98b62\n# addr: 0x86231b5cdcbfe751b9ddcd4bd981fc0a48afe921\n# priv: 2ca054a50b53299ea3949f5362ee1d1cfe6252fbe30bea3651774790983e9348\n```\n\n### Keystore\n\n```python\nfrom thor_devkit.cry import keystore\n\nks = {\n    \"version\": 3,\n    \"id\": \"f437ebb1-5b0d-4780-ae9e-8640178ffd77\",\n    \"address\": \"dc6fa3ec1f3fde763f4d59230ed303f854968d26\",\n    \"crypto\":\n    {\n        \"kdf\": \"scrypt\",\n        \"kdfparams\": {\n            \"dklen\": 32,\n            \"salt\": \"b57682e5468934be81217ad5b14ca74dab2b42c2476864592c9f3b370c09460a\",\n            \"n\": 262144,\n            \"r\": 8,\n            \"p\": 1\n        },\n        \"cipher\": \"aes-128-ctr\",\n        \"ciphertext\": \"88cb876f9c0355a89cad88ee7a17a2179700bc4306eaf78fa67320efbb4c7e31\",\n        \"cipherparams\": {\n            \"iv\": \"de5c0c09c882b3f679876b22b6c5af21\"\n        },\n        \"mac\": \"8426e8a1e151b28f694849cb31f64cbc9ae3e278d02716cf5b61d7ddd3f6e728\"\n    }\n}\npassword = b'123456'\n\n# Decrypt\nprivate_key = keystore.decrypt(ks, password)\n\n# Encrypt\nks_backup = keystore.encrypt(private_key, password)\n```\n\n### Hash the Messages\n```python\nfrom thor_devkit import cry\n\nresult, length = cry.blake2b256([b'hello world'])\nresult2, length = cry.blake2b256([b'hello', b' world'])\n# result == result2\n\nresult, length = cry.keccak256([b'hello world'])\nresult2, length = cry.keccak256([b'hello', b' world'])\n# result == result2\n```\n\n\n### Bloom Filter\n```python\nfrom thor_devkit import Bloom\n\n# Create a bloom filter that can store 100 items.\n_k = Bloom.estimate_k(100)\nb = Bloom(_k)\n\n# Add an item to the bloom filter.\nb.add(bytes('hello world', 'UTF-8'))\n\n# Verify\nb.test(bytes('hello world', 'UTF-8'))\n# True\nb.test(bytes('bye bye blue bird', 'UTF-8'))\n# False\n```\n\n### Transaction\n\n```python\nfrom thor_devkit import cry, transaction\n\n# See: https://docs.vechain.org/thor/learn/transaction-model.html#model\nbody = {\n    \"chainTag\": int('0x4a', 16), # 0x4a/0x27/0xa4 See: https://docs.vechain.org/others/miscellaneous.html#network-identifier\n    \"blockRef\": '0x00000000aabbccdd',\n    \"expiration\": 32,\n    \"clauses\": [\n        {\n            \"to\": '0x7567d83b7b8d80addcb281a71d54fc7b3364ffed',\n            \"value\": 10000,\n            \"data\": '0x000000606060'\n        },\n        {\n            \"to\": '0x7567d83b7b8d80addcb281a71d54fc7b3364ffed',\n            \"value\": 20000,\n            \"data\": '0x000000606060'\n        }\n    ],\n    \"gasPriceCoef\": 128,\n    \"gas\": 21000,\n    \"dependsOn\": None,\n    \"nonce\": 12345678\n}\n\n# Construct an unsigned transaction.\ntx = transaction.Transaction(body)\n\n# Access its properties.\ntx.get_signing_hash() == cry.blake2b256([tx.encode()])[0] # True\n\ntx.get_signature() == None # True\n\ntx.get_origin() == None # True\n\ntx.get_intrinsic_gas() == 37432 # estimate the gas this tx gonna cost.\n\n# Sign the transaction with a private key.\npriv_key = bytes.fromhex('7582be841ca040aa940fff6c05773129e135623e41acce3e0b8ba520dc1ae26a')\nmessage_hash = tx.get_signing_hash()\nsignature = cry.secp256k1.sign(message_hash, priv_key)\n\n# Set the signature on the transaction.\ntx.set_signature(signature)\n\n# Tx origin?\nprint(tx.get_origin())\n# 0xd989829d88b0ed1b06edf5c50174ecfa64f14a64\n\n# Tx id?\nprint(tx.get_id())\n# 0xda90eaea52980bc4bb8d40cb2ff84d78433b3b4a6e7d50b75736c5e3e77b71ec\n\n# Tx encoded into bytes, ready to be sent out.\nencoded_bytes = tx.encode()\n\n# pretty print the encoded bytes.\nprint('0x' + encoded_bytes.hex())\n\n# http POST transaction to send the encoded_bytes to VeChain...\n# See the REST API details:\n# testnet: https://sync-testnet.vechain.org/doc/swagger-ui/\n# mainnet: https://sync-mainnet.vechain.org/doc/swagger-ui/\n```\n\n### Transaction (VIP-191)\n[https://github.com/vechain/VIPs/blob/master/vips/VIP-191.md](https://github.com/vechain/VIPs/blob/master/vips/VIP-191.md)\n\n```python\nfrom thor_devkit import cry, transaction\n\ndelegated_body = {\n    \"chainTag\": 1,\n    \"blockRef\": '0x00000000aabbccdd',\n    \"expiration\": 32,\n    \"clauses\": [\n        {\n            \"to\": '0x7567d83b7b8d80addcb281a71d54fc7b3364ffed',\n            \"value\": 10000,\n            \"data\": '0x000000606060'\n        },\n        {\n            \"to\": '0x7567d83b7b8d80addcb281a71d54fc7b3364ffed',\n            \"value\": 20000,\n            \"data\": '0x000000606060'\n        }\n    ],\n    \"gasPriceCoef\": 128,\n    \"gas\": 21000,\n    \"dependsOn\": None,\n    \"nonce\": 12345678,\n    \"reserved\": {\n        \"features\": 1\n    }\n}\n\ndelegated_tx = transaction.Transaction(delegated_body)\n\n# Indicate it is a delegated Transaction using VIP-191.\nassert delegated_tx.is_delegated() == True\n\n# Sender\naddr_1 = '0xf9ea4ba688d55cc7f0eae0dd62f8271b744637bf'\n\npriv_1 = bytes.fromhex('58e444d4fe08b0f4d9d86ec42f26cf15072af3ddc29a78e33b0ceaaa292bcf6b')\n\n\n# Gas Payer\naddr_2 = '0x34b7538c2a7c213dd34c3ecc0098097d03a94dcb'\n\npriv_2 = bytes.fromhex('0bfd6a863f347f4ef2cf2d09c3db7b343d84bb3e6fc8c201afee62de6381dc65')\n\n\nh = delegated_tx.get_signing_hash() # Sender hash to be signed.\ndh = delegated_tx.get_signing_hash(addr_1) # Gas Payer hash to be signed.\n\n# Sender sign the hash.\n# Gas payer sign the hash.\n# Concat two parts to forge a legal signature.\nsig = cry.secp256k1.sign(h, priv_1) + cry.secp256k1.sign(dh, priv_2)\n\ndelegated_tx.set_signature(sig)\n\nassert delegated_tx.get_origin() == addr_1\nassert delegated_tx.get_delegator() == addr_2\n```\n\n### Sign/Verify Certificate (VIP-192)\n[https://github.com/vechain/VIPs/blob/master/vips/VIP-192.md](https://github.com/vechain/VIPs/blob/master/vips/VIP-192.md)\n\n```python\nfrom thor_devkit import cry\nfrom thor_devkit.cry import secp256k1\nfrom thor_devkit import certificate\n\n# My address.\naddress = '0xd989829d88b0ed1b06edf5c50174ecfa64f14a64'\n# My corresponding private key.\nprivate_key = bytes.fromhex('7582be841ca040aa940fff6c05773129e135623e41acce3e0b8ba520dc1ae26a')\n\n# My cert.\ncert_dict = {\n    'purpose': 'identification',\n    'payload': {\n        'type': 'text',\n        'content': 'fyi'\n    },\n    'domain': 'localhost',\n    'timestamp': 1545035330,\n    'signer': address\n}\n\n# Construct a cert, without signature.\ncert = certificate.Certificate(**cert_dict)\n\n# Sign the cert with my private key.\nsig_bytes = secp256k1.sign(\n    cry.blake2b256([\n        certificate.encode(cert).encode('utf-8')\n    ])[0],\n    private_key\n)\nsignature = '0x' + sig_bytes.hex()\n\n# Mount the signature onto the cert.\ncert_dict['signature'] = signature\n\n# Construct a cert, with signature.\ncert2 = certificate.Certificate(**cert_dict)\n\n# Verify, if verify failed it will throw Exceptions.\ncertificate.verify(cert2)\n```\n\n### ABI\n\nEncode function name and parameters according to ABI.\n\n```python\nfrom thor_devkit import abi\n\nabi_dict = {\n        \"constant\": False,\n        \"inputs\": [\n            {\n                \"name\": \"a1\",\n                \"type\": \"uint256\"\n            },\n            {\n                \"name\": \"a2\",\n                \"type\": \"string\"\n            }\n        ],\n        \"name\": \"f1\",\n        \"outputs\": [\n            {\n                \"name\": \"r1\",\n                \"type\": \"address\"\n            },\n            {\n                \"name\": \"r2\",\n                \"type\": \"bytes\"\n            }\n        ],\n        \"payable\": False,\n        \"stateMutability\": \"nonpayable\",\n        \"type\": \"function\"\n}\n\n# Verify if abi_dict is in good shape.\nf1 = abi.FUNCTION(abi_dict)\n\n# Get a function instance of the abi.\nf = abi.Function(f1)\n\n# Get function selector:\nselector = f.selector.hex()\nselector == '27fcbb2f'\n\n# Encode the function input parameters.\nr = f.encode([1, 'foo'], to_hex=True)\nr == '0x27fcbb2f000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000003666f6f0000000000000000000000000000000000000000000000000000000000'\n\n# Decode function return result according to abi.\ndata = '000000000000000000000000abc000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000003666f6f0000000000000000000000000000000000000000000000000000000000'\n\nr = f.decode(bytes.fromhex(data))\n# {\n#     \"0\": '0xabc0000000000000000000000000000000000001',\n#     \"1\": b'666f6f',\n#     \"r1\": '0xabc0000000000000000000000000000000000001',\n#     \"r2\": b'666f6f'\n# }\n```\n\nDecode logs according to data and topics.\n\n```python\nfrom thor_devkit import abi\n\ne2 = abi.EVENT({\n    \"anonymous\": True,\n    \"inputs\": [\n        {\n            \"indexed\": True,\n            \"name\": \"a1\",\n            \"type\": \"uint256\"\n        },\n        {\n            \"indexed\": False,\n            \"name\": \"a2\",\n            \"type\": \"string\"\n        }\n    ],\n    \"name\": \"E2\",\n    \"type\": \"event\"\n})\n\nee = abi.Event(e2)\n\n# data in hex format.\nr = ee.decode(\n    data=bytes.fromhex('00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000003666f6f0000000000000000000000000000000000000000000000000000000000'),\n    topics=[\n        bytes.fromhex('0000000000000000000000000000000000000000000000000000000000000001')\n    ]\n)\n\n# r == { \"0\": 1, \"1\": \"foo\", \"a1\": 1, \"a2\": \"foo\" }\n```\n\n# Tweak the Code\n\n## Layout\n```\n.\n\u251c\u2500\u2500 LICENSE\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 requirements.txt\n\u2514\u2500\u2500 thor_devkit\n    \u251c\u2500\u2500 __init__.py\n    \u251c\u2500\u2500 abi.py\n    \u251c\u2500\u2500 bloom.py\n    \u251c\u2500\u2500 certificate.py\n    \u251c\u2500\u2500 cry\n    \u2502   \u251c\u2500\u2500 __init__.py\n    \u2502   \u251c\u2500\u2500 address.py\n    \u2502   \u251c\u2500\u2500 blake2b.py\n    \u2502   \u251c\u2500\u2500 hdnode.py\n    \u2502   \u251c\u2500\u2500 keccak.py\n    \u2502   \u251c\u2500\u2500 keystore.py\n    \u2502   \u251c\u2500\u2500 mnemonic.py\n    \u2502   \u251c\u2500\u2500 secp256k1.py\n    \u2502   \u2514\u2500\u2500 utils.py\n    \u251c\u2500\u2500 rlp.py\n    \u2514\u2500\u2500 transaction.py\n```\n\n## Local Development\n```bash\n# install dependencies\nmake install\n# test code\nmake test\n```\n\n## Knowledge\n\n|     Name     | Bytes |                  Description                   |\n| ------------ | ----- | ---------------------------------------------- |\n| private key  | 32    | random number                                  |\n| public key   | 65    | uncompressed, starts with \"04\"                 |\n| address      | 20    | derived from public key                        |\n| keccak256    | 32    | hash                                           |\n| blake2b256   | 32    | hash                                           |\n| message hash | 32    | hash of a message                              |\n| signature    | 65    | signing result, last bit as recovery parameter |\n| seed         | 64    | used to derive bip32 master key                |\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "SDK to interact with VeChain Thor public blockchain.",
    "version": "1.0.0",
    "project_urls": {
        "Documentation": "https://github.com/laalaguer/thor-devkit.py",
        "Homepage": "https://github.com/laalaguer/thor-devkit.py",
        "Issue Tracker": "https://github.com/laalaguer/thor-devkit.py/issues",
        "Source": "https://github.com/laalaguer/thor-devkit.py"
    },
    "split_keywords": [
        "vechain",
        "thor",
        "blockchain",
        "sdk"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "677422697a3aed1c440d8245aec37e52c532720dd353bca0e754f94d3428eab6",
                "md5": "6033c1226ee5a16fdd8c3ac19333f47a",
                "sha256": "af0b13e5e124d7c874239b2f399d2b4805c5fa44c7eb7f795b7e30861600333d"
            },
            "downloads": -1,
            "filename": "cneal_thor_devkit-1.0.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "6033c1226ee5a16fdd8c3ac19333f47a",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.6",
            "size": 40460,
            "upload_time": "2024-07-26T19:33:35",
            "upload_time_iso_8601": "2024-07-26T19:33:35.387275Z",
            "url": "https://files.pythonhosted.org/packages/67/74/22697a3aed1c440d8245aec37e52c532720dd353bca0e754f94d3428eab6/cneal_thor_devkit-1.0.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "578b46eebb09e03c97589d36b569a642ca5f454bfcb1f361f1a2221ef56385a8",
                "md5": "985bd51c5e32b1f69c942fd45d9404b6",
                "sha256": "c67c744291bfc89bf14877b2e6cd8a5a74eaefba44dd4b56877e40a833df84e6"
            },
            "downloads": -1,
            "filename": "cneal_thor_devkit-1.0.0.tar.gz",
            "has_sig": false,
            "md5_digest": "985bd51c5e32b1f69c942fd45d9404b6",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.6",
            "size": 44588,
            "upload_time": "2024-07-26T19:33:37",
            "upload_time_iso_8601": "2024-07-26T19:33:37.295797Z",
            "url": "https://files.pythonhosted.org/packages/57/8b/46eebb09e03c97589d36b569a642ca5f454bfcb1f361f1a2221ef56385a8/cneal_thor_devkit-1.0.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-07-26 19:33:37",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "laalaguer",
    "github_project": "thor-devkit.py",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "requirements": [],
    "lcname": "cneal-thor-devkit"
}
        
Elapsed time: 0.30760s