Unpacking AccountResource in Python

I need some help in the unpacking of bytes which I am getting from gRPC by sending GetAccountStateRequest.

Here https://github.com/egorsmkv/libra-grpc-py/blob/master/examples/account_state.py I have a state returned by the gRPC service. But I have problems when I go further, so at this stage, I want to access a blob value by this code:

    blob = response.response_items[0].get_account_state_response.account_state_with_proof.blob.blob
    info = get_account_resource(blob)
    print(info)

The blob variable has bytes format as we see from the .proto file:

message AccountStateBlob { bytes blob = 1; }

Next, I am trying to unpack these bytes to a python’s namedtuple:

AccountResource = namedtuple('AccountResource',
                             'balance sequence_number authentication_key sent_events_count received_events_count')

def get_account_resource(data):
    print(data)
    print(data.hex())

    s = Struct('< I I s I I')
    o = AccountResource._make(s.unpack(data[:s.size]))
    print(o)

    return {}

And here I have a problem, it is how to correctly unpack these bytes. The unpack’s format from above code returned this result:

(libra-python) yehor@nb:~/Test/libra-python$ python examples/account_state.py 
b'\x01\x00\x00\x00!\x00\x00\x00\x01!}\xa6\xc6\xb3\xe1\x9f\x18%\xcf\xb2gm\xae\xcc\xe3\xbf=\xe0<\xf2fG\xc7\x8d\xf0\x0b7\x1b%\xcc\x97D\x00\x00\x00 \x00\x00\x00K\xb2>\xb0\x92\xa0O\xae}\xeaV\xff\xfb\x882/\x990\x17\xe9\xbb\x8e\x98"\xcbD\x1c\x80\x86-x\xe6\x00\xd1Y\x92\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00'
010000002100000001217da6c6b3e19f1825cfb2676daecce3bf3de03cf26647c78df00b371b25cc9744000000200000004bb23eb092a04fae7dea56fffb88322f993017e9bb8e9822cb441c80862d78e600d159920b000000000000000000000001000000000000000100000000000000
AccountResource(balance=1, sequence_number=33, authentication_key=b'\x01', sent_events_count=3332799777, received_events_count=413131187)
{}

Notes:

  1. AccountResource struct is defined here: https://github.com/libra/libra/blob/5e034dde19a5320d7e2bdc9da25114e816b4454d/types/src/account_config.rs#L68
3 Likes

I am not sure how your unpacking code looks but take a look at https://github.com/libra/libra/blob/5e034dde19a5320d7e2bdc9da25114e816b4454d/types/src/account_config.rs#L129-L159 to see the order and types of the serialized format.
In general integers are serialized in LittleEndian and the address as a vector …

Of course that format can change but I assume that is expected

what is it there?

and how can I calculate self.authentication_key? Inside it is Vec<u8> but how many u8 it has?

fair question, an address is serialized as a vector of bytes. First 32 bits (4 bytes) are the count of bytes, next are all the bytes. The unsigned32 count is in Little Endian format.
Hope this helps

I almost solve the problem with https://pypi.org/project/bitstruct/

Now I can get these parameters: balance, received_events_count, sent_events_count, sequence_number

Pay attention to the address:
000000000000000000000000000000000000000000000000000000000000
You need to consider byte offset before asset_type . Authentication_key not always the address.
Format for AccountResource:

  1. AssetType (from him we are counting) (32 bytes)
  2. AuthenticationKey
  3. Balance
  4. ReceivedEventsCount
  5. SequenceNumber
  6. SentEventsCount

As I understand from the code we are talking about this one: https://github.com/libra/libra/blob/5e034dde19a5320d7e2bdc9da25114e816b4454d/types/src/account_config.rs#L96

The address is added here: https://github.com/libra/libra/blob/5e034dde19a5320d7e2bdc9da25114e816b4454d/types/src/account_config.rs#L178

But with the address, there are other parameters also provided such as module, name, type_params. These parameters also must be in the returned array of bytes.

I sad about AccountResource. There is no such parameters in AccountStateBlob. AccessPath in events.

There is still a need to clarify how the blob is constructing and how to read its in other programming languages.

@phil sees it in this way:

01000000: 1 (mapEntryCount)
21000000: 33 (no of bytes for the map key)
01217da6c6b3e19f1825cfb2676daecce3bf3de03cf26647c78df00b371b25cc97 (map key)
44000000: 68 (no of bytes for the map value)
the rest is the map value, which can be broken down into:
20000000: 32 (length of auth key in bytes)
8cd377191fe0ef113455c8e8d769f0c0147d5bb618bf195c0af31a05fbfd0969 (auth key)
a0acb90300000000: 62500000 (balance)
0100000000000000: 1 (received events)
0400000000000000: 4 (sent events)
0400000000000000: 4 (sequence no)

For others who are interested: I found an implementation that does it that way here:


I also implemented that in Go in my own repository here:

It worked for the Libra accounts that I tried.

Can you explain how to convert the hexidecimal to decimal.

How is
44000000 --> 68
a0acb90300000000 --> 62500000