Add light client chain specifications for Zagros (#78)

light-client/zagros.json and light-client/people-zagros.json are what the
wallet's embedded light client starts from.

The full specifications carry the raw genesis storage, about 3 MB each.
These carry only the genesis state root, which identifies the chain:
- The relay specification adds a checkpoint (lightSyncState) exported by
  the relay node through sync_state_genSyncSpec. It is 7.3 KB.
- People has no checkpoint, because the relay chain's finality covers it.
  It is 732 bytes.

Boot nodes put the /wss addresses on 443 first and keep the plain TCP ones.

scripts/light_client_specs.py writes both files. It checks every state root
by rebuilding the genesis header and comparing its hash with the chain's
genesis hash. A root with one byte changed is refused.

The checkpoint is a trust anchor: a client that starts from a false one can
follow a false chain. It reaches the wallet inside the APK or through
signed/targets.json, never from peers. Targets are re-signed (version 3,
689 files).

The secret scan lets through the four public chain-data keys these files
carry: stateRootHash, finalizedBlockHeader, babeEpochChanges and
grandpaAuthoritySet.
This commit is contained in:
SatoshiQaziMuhammed authored and GitHub committed 2026-11-03 00:00:54 +04:00
1 parent 07aa8b3dd0
commit eb00e49a52
5 files changed
+171 -4

No files matched your search

+4 -2
View File
@@ -57,8 +57,10 @@ jobs:
FOUND=0
echo "Checking for private keys in config files..."
# "account" fields hold public 32-byte account IDs (balance-test fixtures), not private keys
if grep -rn --include="*.json" --include="*.py" -E "(0x[a-fA-F0-9]{64}|-----BEGIN.*PRIVATE)" . | grep -v node_modules | grep -v "chainId\|genesisHash\|parentId\|currencyIdScale\|typeAlias\|signedExtensions\|\"account\":"; then
# "account" fields hold public 32-byte account IDs (balance-test fixtures), not private keys.
# The light client specifications carry public chain data: the genesis state root and the
# relay chain checkpoint (an encoded finalized header, BABE epochs, GRANDPA authorities).
if grep -rn --include="*.json" --include="*.py" -E "(0x[a-fA-F0-9]{64}|-----BEGIN.*PRIVATE)" . | grep -v node_modules | grep -v "chainId\|genesisHash\|parentId\|currencyIdScale\|typeAlias\|signedExtensions\|\"account\":\|\"stateRootHash\":\|\"finalizedBlockHeader\":\|\"babeEpochChanges\":\|\"grandpaAuthoritySet\":"; then
echo "::error::Possible private key found"
FOUND=1
fi
+23
View File
@@ -0,0 +1,23 @@
{
"bootNodes": [
"/dns/zagros-people-bootnode.pezkuwichain.io/tcp/443/wss/p2p/12D3KooWLnDS5p5bvMzfco2BR4arKRbtpqoZwRvLKgCdzgVpuEQG",
"/ip4/217.77.6.126/tcp/30535/p2p/12D3KooWLnDS5p5bvMzfco2BR4arKRbtpqoZwRvLKgCdzgVpuEQG",
"/ip4/164.68.110.49/tcp/30535/p2p/12D3KooW9rHfLv4za95C4fPGTUzNk531zRrjaXYJY3pijMEYd3xB"
],
"chainType": "Live",
"codeSubstitutes": {},
"genesis": {
"stateRootHash": "0xcaffff9ce4cf02e84a963a8b2ff27834f4d2385a1dc3b01a23551da8515d6c98"
},
"id": "people-zagros",
"name": "Zagros People",
"para_id": 1004,
"properties": {
"ss58Format": 42,
"tokenDecimals": 12,
"tokenSymbol": "HEZ"
},
"protocolId": null,
"relay_chain": "zagros",
"telemetryEndpoints": null
}
+30
View File
@@ -0,0 +1,30 @@
{
"badBlocks": null,
"bootNodes": [
"/dns/zagros-bootnode-0.pezkuwichain.io/tcp/443/wss/p2p/12D3KooWHKMr39ubvsigZXdVJzr7nnMp5YTzMywKuTzPDo3Xf1tZ",
"/dns/zagros-bootnode-1.pezkuwichain.io/tcp/443/wss/p2p/12D3KooWH6tzPuoFeyf8n6gNmus9Q3CdEGEVUvbduUnPTqJcLRLC",
"/dns/zagros-bootnode-0.pezkuwichain.io/tcp/30333/p2p/12D3KooWHKMr39ubvsigZXdVJzr7nnMp5YTzMywKuTzPDo3Xf1tZ",
"/dns/zagros-bootnode-1.pezkuwichain.io/tcp/30333/p2p/12D3KooWH6tzPuoFeyf8n6gNmus9Q3CdEGEVUvbduUnPTqJcLRLC"
],
"chainType": "Live",
"codeSubstitutes": {},
"forkBlocks": null,
"genesis": {
"stateRootHash": "0xf34e48ee64d91316874ccb2186a111d4c5b4ff5f07a302472eb781929a42996f"
},
"id": "zagros",
"lightSyncState": {
"babeEpochChanges": "0x0484e33c00890c931035c417885ef185862d12f8e5ef1a1b2697b79d3040a6a17689300800017076d11100000000c878d1110000000004b787118322f0bc70ea799cf7970ea9af917ab3573264d58c3b90ad0cde3848358432080001c878d11100000000207bd1110000000004c1317066f50e2ad43e0b2f892dfdaa7a8148aee518235774a71ec528c26a780e7a34080001207bd11100000000787dd1110000000000000c84e33c00890c931035c417885ef185862d12f8e5ef1a1b2697b79d3040a6a176893008000193030000000000007076d1110000000058020000000000003822a5e0172444e42a1ea3e29830c83a6e0a84a33263cfe2a7008871b001a8e74b0100000000000000507fc640a1bb08b2e118846de14705579dfe58672cf38f21db5752926acd94170100000000000000ac680114b0cf7808f05deeddebd067ed520eb71f9dccca4d3290a91760950e740100000000000000620f7e55a0af1d74edfd807d77e2f125ed28a1fcd865699da5897e6cf7d8e93a01000000000000004e4977fb1f2802b5c49b59b0154ac8f5f888bb72d91a21b0b84cb896a900ff5401000000000000000c5c662c5202d3791ea8d5a19f8121d2422790dbe46420eb83d6f12d90fb6f670100000000000000c015d853b41e00914525c852fc89bad17ed90a8df1a6be9bd7e945967e59de6e01000000000000008c54eb78ec5f530317c3f74ec43e3c0d14460755377b147be26f6e25ace5f03d01000000000000002c6fd021135a0de764bd7a26bad6b5a329c00d3195182f2f29e1694b4e46ab3901000000000000006896d03c0d08da73257a98332f418dbd84dedb10515a6cb20608b065953b833f01000000000000003a35ad97f29553dbf45438c7aabb1c6703cda4edfe31a6d8e97d75e77c7e625801000000000000001e307845454d1930a44728d3a4b0c3a8a796ca7864f02337506d95bbba23a64d010000000000000026f1a959083449c89877ab91a027133806ea41a7363dccd66b751c4f31b3ab1a0100000000000000f2e648c3df9281cbe3514b6a4c244a4d3d10bcf6dcd9aa8446149d8fe5cf821a01000000000000002167c0d326e6ac3175ddecf4616bba618737717364abbc103bca86e056b1e4f30100000000000000040000000000000002b787118322f0bc70ea799cf7970ea9af917ab3573264d58c3b90ad0cde38483584320800019403000000000000c878d1110000000058020000000000003822a5e0172444e42a1ea3e29830c83a6e0a84a33263cfe2a7008871b001a8e74b0100000000000000507fc640a1bb08b2e118846de14705579dfe58672cf38f21db5752926acd94170100000000000000ac680114b0cf7808f05deeddebd067ed520eb71f9dccca4d3290a91760950e740100000000000000620f7e55a0af1d74edfd807d77e2f125ed28a1fcd865699da5897e6cf7d8e93a01000000000000004e4977fb1f2802b5c49b59b0154ac8f5f888bb72d91a21b0b84cb896a900ff5401000000000000000c5c662c5202d3791ea8d5a19f8121d2422790dbe46420eb83d6f12d90fb6f670100000000000000c015d853b41e00914525c852fc89bad17ed90a8df1a6be9bd7e945967e59de6e01000000000000008c54eb78ec5f530317c3f74ec43e3c0d14460755377b147be26f6e25ace5f03d01000000000000002c6fd021135a0de764bd7a26bad6b5a329c00d3195182f2f29e1694b4e46ab3901000000000000006896d03c0d08da73257a98332f418dbd84dedb10515a6cb20608b065953b833f01000000000000003a35ad97f29553dbf45438c7aabb1c6703cda4edfe31a6d8e97d75e77c7e625801000000000000001e307845454d1930a44728d3a4b0c3a8a796ca7864f02337506d95bbba23a64d010000000000000026f1a959083449c89877ab91a027133806ea41a7363dccd66b751c4f31b3ab1a0100000000000000f2e648c3df9281cbe3514b6a4c244a4d3d10bcf6dcd9aa8446149d8fe5cf821a01000000000000005e6e1acac124ee50f061c35e668f888f6bb3d51af94aeaad312bd3052ea797c50100000000000000040000000000000002c1317066f50e2ad43e0b2f892dfdaa7a8148aee518235774a71ec528c26a780e7a340800019503000000000000207bd1110000000058020000000000003822a5e0172444e42a1ea3e29830c83a6e0a84a33263cfe2a7008871b001a8e74b0100000000000000507fc640a1bb08b2e118846de14705579dfe58672cf38f21db5752926acd94170100000000000000ac680114b0cf7808f05deeddebd067ed520eb71f9dccca4d3290a91760950e740100000000000000620f7e55a0af1d74edfd807d77e2f125ed28a1fcd865699da5897e6cf7d8e93a01000000000000004e4977fb1f2802b5c49b59b0154ac8f5f888bb72d91a21b0b84cb896a900ff5401000000000000000c5c662c5202d3791ea8d5a19f8121d2422790dbe46420eb83d6f12d90fb6f670100000000000000c015d853b41e00914525c852fc89bad17ed90a8df1a6be9bd7e945967e59de6e01000000000000008c54eb78ec5f530317c3f74ec43e3c0d14460755377b147be26f6e25ace5f03d01000000000000002c6fd021135a0de764bd7a26bad6b5a329c00d3195182f2f29e1694b4e46ab3901000000000000006896d03c0d08da73257a98332f418dbd84dedb10515a6cb20608b065953b833f01000000000000003a35ad97f29553dbf45438c7aabb1c6703cda4edfe31a6d8e97d75e77c7e625801000000000000001e307845454d1930a44728d3a4b0c3a8a796ca7864f02337506d95bbba23a64d010000000000000026f1a959083449c89877ab91a027133806ea41a7363dccd66b751c4f31b3ab1a0100000000000000f2e648c3df9281cbe3514b6a4c244a4d3d10bcf6dcd9aa8446149d8fe5cf821a01000000000000004015e4d23a08bf3953729cdffed4287e49b959cfecf4a7056f4d725ab1d632ff0100000000000000040000000000000002",
"babeFinalizedBlockWeight": 133663,
"finalizedBlockHeader": "0x61d5374b2608eba8044eb6b53bbb1885f9b4f7435d748aafa3274569cb61d3fc2ed92000a7be66700b76965c09b6ed96afeee418e1ec074336b419a5466b02aa3cfc253139c855a50829b03d89a771899be7eba352bb5e73243665554fcdb7e2691cd9940c0642414245b501030d000000eb7ad11100000000f0c61d37049856397cf9983bf17937f7915b675985d6a1b76743ee86a4689b2a23036a92220bc8f2317ea673f22df1721c94e5592eab0e0e75001b3e9896580f3ba6422cae995ae9fcf59beecb51ead04b9795b515bfee87e7d65f745371470f04424545468403342ba73d049de92f20b1c8217bc2aedc3728da90c771056a18cd3804d002b2a705424142450101a68542e128a2b23d7d34f9d9a0bd190788be671a87ce8139d78c72eb3079cb6e5bfc31392d235d830802ccb35c8c64812ab64bda9c66adec99ce7df84b995781",
"grandpaAuthoritySet": "0x38140ec6acc3d2ccfe8017c2b87a0dfe634c6de1104574059e3ba05cdbb6190f080100000000000000e78c542579cfcb4bf781268a57c7ae19abe426ed075f298f1ead033e901da882010000000000000047d1e266d0f8411dc03808acbaf330499c03a288f0dd80d0b7a558e1ee7b6e8f01000000000000001cee1e8b20ef554efaa29ac5a2c2418503df3a4449a769f3649a2add036bd38f01000000000000002600c4b144906b2dc3ba8aefca015a8532e5589b11b164439fb0b4fb004383aa01000000000000000d3be87d022659068339f82bb458976f83b7c75287b842a2dc179d556b34fbff01000000000000003e6aae6e3c55c25f0b6765818b513ccceac769d371a4bb401240282d251151e7010000000000000037cd7e2689782aa1d5f52ed24bd9c4bcf8f811effbefac2039a027a51f33a5950100000000000000b34d50cbb391e36f5820fe8d4c572b8c20e3b7ce82dfbdac08ac800b90554d4301000000000000000e96449f0667776ca4507b30fef3e4168f20da1acb0dd57e71ecca74d7e7ace50100000000000000c57688bc9175f1ea6e2cf7dd8a3593ca6b2764b1b78165c8e8c6b791f66b48a70100000000000000fb6f3bc5a49916d1917500d3ff1882c525f2d20d32f16d5429ec1595e05db18201000000000000007d4567a4100b9fd2db6d741a60aef2693f6ca1c875a588036ab2b7cd3d449b63010000000000000087f3c60d6f12aeee640840f82b9deb899d287dffc605a9a115d46dc84f4739b40100000000000000040000000000000000014b36080000100000000000000000d12a030001000000000000008d3d03000200000000000000ae6b05000300000000000000d68e0500"
},
"name": "Zagros",
"properties": {
"ss58Format": 42,
"tokenDecimals": 12,
"tokenSymbol": "HEZ"
},
"protocolId": null,
"telemetryEndpoints": null
}
+112
View File
@@ -0,0 +1,112 @@
#!/usr/bin/env python3
"""Chain specifications for the wallet's light client, written to light-client/.
The full specifications carry the raw genesis storage, about 3 MB each. The light client needs
none of it: the genesis state root identifies the chain, and the relay chain starts from a
checkpoint (lightSyncState) that the relay node exports with sync_state_genSyncSpec. What is
written here is that trimmed form, with the boot nodes below.
The checkpoint is a trust anchor: a client that starts from a false one can follow a false
chain. It is taken from our own relay node and reaches the wallet only inside the APK or through
signed/targets.json, never from peers. Every state root is checked against its chain's genesis
hash before anything is written.
light_client_specs.py --relay-rpc URL --people-rpc URL --people-spec FILE
"""
import argparse
import hashlib
import json
import sys
import urllib.request
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
OUT = REPO / "light-client"
# Secure WebSockets on 443 first: they look like any HTTPS WebSocket where plain libp2p TCP is
# filtered. The TCP addresses stay for networks that filter nothing.
ZAGROS_BOOT_NODES = [
"/dns/zagros-bootnode-0.pezkuwichain.io/tcp/443/wss/p2p/12D3KooWHKMr39ubvsigZXdVJzr7nnMp5YTzMywKuTzPDo3Xf1tZ",
"/dns/zagros-bootnode-1.pezkuwichain.io/tcp/443/wss/p2p/12D3KooWH6tzPuoFeyf8n6gNmus9Q3CdEGEVUvbduUnPTqJcLRLC",
"/dns/zagros-bootnode-0.pezkuwichain.io/tcp/30333/p2p/12D3KooWHKMr39ubvsigZXdVJzr7nnMp5YTzMywKuTzPDo3Xf1tZ",
"/dns/zagros-bootnode-1.pezkuwichain.io/tcp/30333/p2p/12D3KooWH6tzPuoFeyf8n6gNmus9Q3CdEGEVUvbduUnPTqJcLRLC",
]
PEOPLE_ZAGROS_BOOT_NODES = [
"/dns/zagros-people-bootnode.pezkuwichain.io/tcp/443/wss/p2p/12D3KooWLnDS5p5bvMzfco2BR4arKRbtpqoZwRvLKgCdzgVpuEQG",
"/ip4/217.77.6.126/tcp/30535/p2p/12D3KooWLnDS5p5bvMzfco2BR4arKRbtpqoZwRvLKgCdzgVpuEQG",
"/ip4/164.68.110.49/tcp/30535/p2p/12D3KooW9rHfLv4za95C4fPGTUzNk531zRrjaXYJY3pijMEYd3xB",
]
def rpc(url: str, method: str, params: list):
request = urllib.request.Request(
url,
json.dumps(
{"jsonrpc": "2.0", "id": 1, "method": method, "params": params}
).encode(),
{"Content-Type": "application/json"},
)
with urllib.request.urlopen(request, timeout=120) as response:
reply = json.load(response)
if "error" in reply:
sys.exit(f"{url} {method}: {reply['error']}")
return reply["result"]
def genesis_state_root(url: str) -> str:
"""The genesis state root, checked by rebuilding the genesis header and hashing it."""
genesis_hash = rpc(url, "chain_getBlockHash", [0])
header = rpc(url, "chain_getHeader", [genesis_hash])
if header["digest"]["logs"]:
sys.exit(f"{url}: genesis header has digest logs; this check assumes none")
encoded = (
bytes(32) # parent hash
+ b"\x00" # compact block number 0
+ bytes.fromhex(header["stateRoot"][2:])
+ bytes.fromhex(header["extrinsicsRoot"][2:])
+ b"\x00" # empty digest
)
rebuilt = "0x" + hashlib.blake2b(encoded, digest_size=32).hexdigest()
if rebuilt != genesis_hash:
sys.exit(f"{url}: rebuilt genesis hash {rebuilt} is not {genesis_hash}")
return header["stateRoot"]
def write(name: str, spec: dict) -> None:
path = OUT / name
path.write_text(json.dumps(spec, indent=2) + "\n")
print(f"{path.relative_to(REPO)}: {path.stat().st_size} bytes")
def main():
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("--relay-rpc", required=True)
parser.add_argument("--people-rpc", required=True)
parser.add_argument(
"--people-spec", required=True, help="full People chain specification"
)
args = parser.parse_args()
OUT.mkdir(exist_ok=True)
relay = rpc(args.relay_rpc, "sync_state_genSyncSpec", [True])
if not relay.get("lightSyncState"):
sys.exit(f"{args.relay_rpc}: the exported specification has no checkpoint")
relay["genesis"] = {"stateRootHash": genesis_state_root(args.relay_rpc)}
relay["bootNodes"] = ZAGROS_BOOT_NODES
# Teyrchains have no checkpoint: the relay chain's finality covers them.
people = json.loads(Path(args.people_spec).read_text())
if people.get("relay_chain") != relay["id"]:
sys.exit(
f"People names relay chain {people.get('relay_chain')!r}, not {relay['id']!r}"
)
people["genesis"] = {"stateRootHash": genesis_state_root(args.people_rpc)}
people["bootNodes"] = PEOPLE_ZAGROS_BOOT_NODES
write("zagros.json", relay)
write("people-zagros.json", people)
if __name__ == "__main__":
main()
+2 -2
View File
@@ -1,9 +1,9 @@
{
"signed": "eyJleHBpcmVzIjoxODI1MDI5MzkzLCJmaWxlcyI6eyJhc3NldHMvZXZtL3YxL2Fzc2V0cy5qc29uIjoiYmI2ZjliYWNlMmY2Y2Q5NjBlMjYyM2Q5ZWE0MmM2OTE3MzUxOWQyZmE5YWVhNWRkMzEyNzA5Njc2ZWExMDdkYSIsImFzc2V0cy9ldm0vdjEvYXNzZXRzX2Rldi5qc29uIjoiNDJhMThhYTUxZTEwZmY0NjllOTYxMGJlMzNmOThkZjhlYWM5NjFhZTVmZDc2YWRhZTNmZThiNzhlZGVlYzIxNiIsImFzc2V0cy9ldm0vdjIvYXNzZXRzLmpzb24iOiJhZWNjZjRlZTE5ODhiOTI0OTRmMWZkZDBjYTI0OGYzYjlmYjE2NmFkZTAyNjRhM2IzZjRmYWYzZDc1MjhhYzBmIiwiYXNzZXRzL2V2bS92Mi9hc3NldHNfZGV2Lmpzb24iOiI2YmZlZDBmMzU2M2YxMDdkNDViZWM0ZjQ3MjYzYWUyYzMyMjA3ZjQ1ZGRhNmQwNmUxNDczNmFkNWM2OTYzYmYzIiwiYXNzZXRzL2V2bS92My9hc3NldHMuanNvbiI6ImM0NDc4NWRiOTkyZjE0NTA3NTFkY2FjOGI1MTVlOTg0NzRlYTA0YTMyZTE4MjgwYjBmOWEyNjNkNzNhM2RjYzUiLCJhc3NldHMvZXZtL3YzL2Fzc2V0c19kZXYuanNvbiI6ImUyMDc5Y2VjNTkxMGQ0NDE0MzBiNzc0ZmYxMjJkN2E2MGI1OTIwMDZlZjE3NmI4NDAzMTE2ZWMyMmM1YTYyNTEiLCJhc3NldHMvc2xpcDQ0Lmpzb24iOiI2MDAyM2IzN2MxMDE4NDdjNzJmMzZmMDVmNTJlNmE4Y2U0Mzk1NjU5ZDZhOTg0ZmE5YjMyYTFiMzlkYWJjODUwIiwiYmFubmVycy92Mi9jb250ZW50L2Fzc2V0cy9iYW5uZXJzLmpzb24iOiI1Yjg3NGMyZjdiNGE3YTJmZDY0Nzc2ZjVhNDI4YTljMTUzMTJjZmNjZDUyMmNlZDRjZjkzMjYxNjFjOTFiZWNiIiwiYmFubmVycy92Mi9jb250ZW50L2Fzc2V0cy9iYW5uZXJzX2Rldi5qc29uIjoiNWI4NzRjMmY3YjRhN2EyZmQ2NDc3NmY1YTQyOGE5YzE1MzEyY2ZjY2Q1MjJjZWQ0Y2Y5MzI2MTYxYzkxYmVjYiIsImJhbm5lcnMvdjIvY29udGVudC9hc3NldHMvbG9jYWxpemVkL2VuLmpzb24iOiI5OTFjOWM1ZDM5OTUyYTdhNGE2ZDk0Yjg2NzAzYTg0NWUwNDQxMTIzNjZkNzg1MDdmNmNlYjA2YzU1OGU1YWQyIiwiYmFubmVycy92Mi9jb250ZW50L2Fzc2V0cy9sb2NhbGl6ZWQva3UuanNvbiI6IjdkMTExOWY1Mzg2MWE5MTY5MzZjNmI0Njk0NDJlMDk5Mjc2ODY4OTU2NTRiYjg1NDkyZDhjNjhkNjU1NDI3MTMiLCJiYW5uZXJzL3YyL2NvbnRlbnQvYXNzZXRzL2xvY2FsaXplZC90ci5qc29uIjoiNDc2MWU3MWY5NmM1NWIwZmE2MmIxM2M2NmE0OGY1ZGM4YjU1MzhjZTcyNjNmN2JjOGVlNjVhNjg2MThlOWQ0NSIsImJhbm5lcnMvdjIvY29udGVudC9hc3NldHMvbG9jYWxpemVkX2Rldi9lbi5qc29uIjoiOTkxYzljNWQzOTk1MmE3YTRhNmQ5NGI4NjcwM2E4NDVlMDQ0MTEyMzY2ZDc4NTA3ZjZjZWIwNmM1NThlNWFkMiIsImJhbm5lcnMvdjIvY29udGVudC9hc3NldHMvbG9jYWxpemVkX2Rldi9rdS5qc29uIjoiN2QxMTE5ZjUzODYxYTkxNjkzNmM2YjQ2OTQ0MmUwOTkyNzY4Njg5NTY1NGJiODU0OTJkOGM2OGQ2NTU0MjcxMyIsImJhbm5lcnMvdjIvY29udGVudC9hc3NldHMvbG9jYWxpemVkX2Rldi90ci5qc29uIjoiNDc2MWU3MWY5NmM1NWIwZmE2MmIxM2M2NmE0OGY1ZGM4YjU1MzhjZTcyNjNmN2JjOGVlNjVhNjg2MThlOWQ0NSIsImJhbm5lcnMvdjIvY29udGVudC9kYXBwcy9iYW5uZXJzLmpzb24iOiJjZTk0NDk0ZjE3YjMzNjJjODQxOGZhYjk4OWZiMDc0NzA5YzVhMWVmYmU5Zjg0N2RjYzE0OGFiZjBkMmFjZjA5IiwiYmFubmVycy92Mi9jb250ZW50L2RhcHBzL2Jhbm5lcnNfZGV2Lmpzb24iOiJjZTk0NDk0ZjE3YjMzNjJjODQxOGZhYjk4OWZiMDc0NzA5YzVhMWVmYmU5Zjg0N2RjYzE0OGFiZjBkMmFjZjA5IiwiYmFubmVycy92Mi9jb250ZW50L2RhcHBzL2xvY2FsaXplZC9lbi5qc29uIjoiMTZhMzUyYzE0Y2QzYjMwYzVmZTlkMmI4MmY0NWYxMjhmMzUzZDEwYWZmMjExOWQwMDNjMjliNTE2MGIzNDI3YSIsImJhbm5lcnMvdjIvY29udGVudC9kYXBwcy9sb2NhbGl6ZWQva3UuanNvbiI6ImUyZjU1YTMzN2U1ZWFlMzQzOWVlNDg4ODI0NzQxMzc2OWFmMzAzNDRmMTRmMjg0Y2UxZjdlOWQ3NzA0Y2IyZDUiLCJiYW5uZXJzL3YyL2NvbnRlbnQvZGFwcHMvbG9jYWxpemVkX2Rldi9lbi5qc29uIjoiMTZhMzUyYzE0Y2QzYjMwYzVmZTlkMmI4MmY0NWYxMjhmMzUzZDEwYWZmMjExOWQwMDNjMjliNTE2MGIzNDI3YSIsImJhbm5lcnMvdjIvY29udGVudC9kYXBwcy9sb2NhbGl6ZWRfZGV2L2t1Lmpzb24iOiJlMmY1NWEzMzdlNWVhZTM0MzllZTQ4ODgyNDc0MTM3NjlhZjMwMzQ0ZjE0ZjI4NGNlMWY3ZTlkNzcwNGNiMmQ1IiwiY2hhaW5zL2NoYWlucy5qc29uIjoiMTM5OWE3Yjg2NTNhYmY3MWVkOTIxODg3ZGFmODRhN2I1ZTY0ZTcxMTUyMjA0NGFjYmQwMTk0ZWExZjRjYTYxZiIsImNoYWlucy9jaGFpbnNfZGV2Lmpzb24iOiJkNWI4NzhkNjY5NDIzODBmN2JjYWZiOTQ2YzRmYjlkOTkzNDA5NDc3YWFkMTljOTM2MDQ0N2MwNDM0NjNiY2ZjIiwiY2hhaW5zL3Blemt1d2ljaGFpbi9jaGFpbnMuanNvbiI6ImMwOWIyYmU5N2ZjMTRhOTM4ZGE1MDVhZmY2NTMyMzQxM2ExNjA2NzQwZTg1ODkxOTY4OWI5NDdkMmIyZmUxZDMiLCJjaGFpbnMvdHlwZXMvYWx0YWlyLmpzb24iOiIxNjg3ZTkyNGQyMjcxZmQ3NjhjYWI3NWQ0OTYxYTY3YzkwYTAwNTNkMTE4ZjFkOGI1YWFlY2EwM2NjOTUwMTQzIiwiY2hhaW5zL3R5cGVzL2JpZnJvc3QuanNvbiI6ImZmMmVjMjdiYzIyMzhmOTYyMWI5MTExYjExMDI1YTY1NjRiNWY2YWVhMzE4ZjE3NzVhNjAyOTUwNDliNDQ0MGEiLCJjaGFpbnMvdHlwZXMvY2FsYW1hcmkuanNvbiI6ImQ1MTUxZGRhMmQ1OWMyMjM5YTAwNjQ0YjMxMjc0NWQ2NDYyMTE0ZmE2YTQ1ZGQ1NDkxZGY4YjkyNDU4ZTE0NmIiLCJjaGFpbnMvdHlwZXMvZGVmYXVsdC5qc29uIjoiNTYxNjA5M2ZjMWI2NDY5MTZkY2E1YjljMzRjMThjZGRjMDUxMDE1NmMwYjgxNTAwOGMyODA5OGM1ZGYzYmY3MyIsImNoYWlucy90eXBlcy9lZGdld2FyZS5qc29uIjoiMjAwZTg4NTE5ZDJlMjMxOTcwMDVjODkyMzYyMGM1Zjg4NmUxMmY3NDNhYWY2YjEyOTdiNDlhOWRmMGQ2Y2NkNSIsImNoYWlucy90eXBlcy9rYXJ1cmEuanNvbiI6IjdkZTZhMjcwMWRiODQ2MmFmYmIyMmVlMGMyNmU2NGI3MDY2N2YyMTM4MTE3ZWNhMjVhNDJmMWY3Mjk1NjQ3MDQiLCJjaGFpbnMvdHlwZXMva2lsdC5qc29uIjoiYmE2N2M3OGEwNGUwYWI5YTI1ZDcxMWY2YzRjZTFiNTEyMjYyMWEyMDdiYmU1MzI1ZjdhOTE3NjlkMTkwMjMwZSIsImNoYWlucy90eXBlcy9rdXNhbWEuanNvbiI6ImZjYzE2OWRjZDQ5OThkMjM3YTAwZmE4NmI3ODI3YzM1ZjhhZTdlNDFjMDYyNmRmNjRiZmQyM2QyMTI1NGFlNWMiLCJjaGFpbnMvdHlwZXMvbW9vbmJhc2UuanNvbiI6IjFkMjVkZjc0YjNlNDIyZjcxOGYxOTdhZGUwNWNlYTZjMTdhOWM3ZWZiMmIxYjVjOTlhMGRiOWExNzAyYjlhODMiLCJjaGFpbnMvdHlwZXMvbW9vbnJpdmVyLmpzb24iOiJjY2E5OTMzNWFlMjU3MmE2ZjRmYmZhMmFhMjNkZDgxMjIxY2NmNGQ1M2M0YjNkMGE1OGUxZjAwYjRjYjRmM2MxIiwiY2hhaW5zL3R5cGVzL3BhcmFsbGVsLmpzb24iOiJiN2ZiNTIyYWFjNGZjODdkMWYyZWNkMjg4NTVlYjcyZmI3ZTEzM2Q1ZjQyYWEwNGQ1ZTJkYjI5MWUxOTdjOGVmIiwiY2hhaW5zL3R5cGVzL3Blemt1d2kuanNvbiI6IjE0NjZkOGU1NDMwNzM4ZDNmZjE1NWQ2NTdhZDlkNTE0MzUzYzIxYjMyYTBmMjc2MTg0MmE0MDkxNWU3MTJhNDMiLCJjaGFpbnMvdHlwZXMvcG9sa2Fkb3QuanNvbiI6IjNmN2ULine truncated
"signed": "eyJleHBpcmVzIjoxODI1MTgzMDkwLCJmaWxlcyI6eyJhc3NldHMvZXZtL3YxL2Fzc2V0cy5qc29uIjoiYmI2ZjliYWNlMmY2Y2Q5NjBlMjYyM2Q5ZWE0MmM2OTE3MzUxOWQyZmE5YWVhNWRkMzEyNzA5Njc2ZWExMDdkYSIsImFzc2V0cy9ldm0vdjEvYXNzZXRzX2Rldi5qc29uIjoiNDJhMThhYTUxZTEwZmY0NjllOTYxMGJlMzNmOThkZjhlYWM5NjFhZTVmZDc2YWRhZTNmZThiNzhlZGVlYzIxNiIsImFzc2V0cy9ldm0vdjIvYXNzZXRzLmpzb24iOiJhZWNjZjRlZTE5ODhiOTI0OTRmMWZkZDBjYTI0OGYzYjlmYjE2NmFkZTAyNjRhM2IzZjRmYWYzZDc1MjhhYzBmIiwiYXNzZXRzL2V2bS92Mi9hc3NldHNfZGV2Lmpzb24iOiI2YmZlZDBmMzU2M2YxMDdkNDViZWM0ZjQ3MjYzYWUyYzMyMjA3ZjQ1ZGRhNmQwNmUxNDczNmFkNWM2OTYzYmYzIiwiYXNzZXRzL2V2bS92My9hc3NldHMuanNvbiI6ImM0NDc4NWRiOTkyZjE0NTA3NTFkY2FjOGI1MTVlOTg0NzRlYTA0YTMyZTE4MjgwYjBmOWEyNjNkNzNhM2RjYzUiLCJhc3NldHMvZXZtL3YzL2Fzc2V0c19kZXYuanNvbiI6ImUyMDc5Y2VjNTkxMGQ0NDE0MzBiNzc0ZmYxMjJkN2E2MGI1OTIwMDZlZjE3NmI4NDAzMTE2ZWMyMmM1YTYyNTEiLCJhc3NldHMvc2xpcDQ0Lmpzb24iOiI2MDAyM2IzN2MxMDE4NDdjNzJmMzZmMDVmNTJlNmE4Y2U0Mzk1NjU5ZDZhOTg0ZmE5YjMyYTFiMzlkYWJjODUwIiwiYmFubmVycy92Mi9jb250ZW50L2Fzc2V0cy9iYW5uZXJzLmpzb24iOiI1Yjg3NGMyZjdiNGE3YTJmZDY0Nzc2ZjVhNDI4YTljMTUzMTJjZmNjZDUyMmNlZDRjZjkzMjYxNjFjOTFiZWNiIiwiYmFubmVycy92Mi9jb250ZW50L2Fzc2V0cy9iYW5uZXJzX2Rldi5qc29uIjoiNWI4NzRjMmY3YjRhN2EyZmQ2NDc3NmY1YTQyOGE5YzE1MzEyY2ZjY2Q1MjJjZWQ0Y2Y5MzI2MTYxYzkxYmVjYiIsImJhbm5lcnMvdjIvY29udGVudC9hc3NldHMvbG9jYWxpemVkL2VuLmpzb24iOiI5OTFjOWM1ZDM5OTUyYTdhNGE2ZDk0Yjg2NzAzYTg0NWUwNDQxMTIzNjZkNzg1MDdmNmNlYjA2YzU1OGU1YWQyIiwiYmFubmVycy92Mi9jb250ZW50L2Fzc2V0cy9sb2NhbGl6ZWQva3UuanNvbiI6IjdkMTExOWY1Mzg2MWE5MTY5MzZjNmI0Njk0NDJlMDk5Mjc2ODY4OTU2NTRiYjg1NDkyZDhjNjhkNjU1NDI3MTMiLCJiYW5uZXJzL3YyL2NvbnRlbnQvYXNzZXRzL2xvY2FsaXplZC90ci5qc29uIjoiNDc2MWU3MWY5NmM1NWIwZmE2MmIxM2M2NmE0OGY1ZGM4YjU1MzhjZTcyNjNmN2JjOGVlNjVhNjg2MThlOWQ0NSIsImJhbm5lcnMvdjIvY29udGVudC9hc3NldHMvbG9jYWxpemVkX2Rldi9lbi5qc29uIjoiOTkxYzljNWQzOTk1MmE3YTRhNmQ5NGI4NjcwM2E4NDVlMDQ0MTEyMzY2ZDc4NTA3ZjZjZWIwNmM1NThlNWFkMiIsImJhbm5lcnMvdjIvY29udGVudC9hc3NldHMvbG9jYWxpemVkX2Rldi9rdS5qc29uIjoiN2QxMTE5ZjUzODYxYTkxNjkzNmM2YjQ2OTQ0MmUwOTkyNzY4Njg5NTY1NGJiODU0OTJkOGM2OGQ2NTU0MjcxMyIsImJhbm5lcnMvdjIvY29udGVudC9hc3NldHMvbG9jYWxpemVkX2Rldi90ci5qc29uIjoiNDc2MWU3MWY5NmM1NWIwZmE2MmIxM2M2NmE0OGY1ZGM4YjU1MzhjZTcyNjNmN2JjOGVlNjVhNjg2MThlOWQ0NSIsImJhbm5lcnMvdjIvY29udGVudC9kYXBwcy9iYW5uZXJzLmpzb24iOiJjZTk0NDk0ZjE3YjMzNjJjODQxOGZhYjk4OWZiMDc0NzA5YzVhMWVmYmU5Zjg0N2RjYzE0OGFiZjBkMmFjZjA5IiwiYmFubmVycy92Mi9jb250ZW50L2RhcHBzL2Jhbm5lcnNfZGV2Lmpzb24iOiJjZTk0NDk0ZjE3YjMzNjJjODQxOGZhYjk4OWZiMDc0NzA5YzVhMWVmYmU5Zjg0N2RjYzE0OGFiZjBkMmFjZjA5IiwiYmFubmVycy92Mi9jb250ZW50L2RhcHBzL2xvY2FsaXplZC9lbi5qc29uIjoiMTZhMzUyYzE0Y2QzYjMwYzVmZTlkMmI4MmY0NWYxMjhmMzUzZDEwYWZmMjExOWQwMDNjMjliNTE2MGIzNDI3YSIsImJhbm5lcnMvdjIvY29udGVudC9kYXBwcy9sb2NhbGl6ZWQva3UuanNvbiI6ImUyZjU1YTMzN2U1ZWFlMzQzOWVlNDg4ODI0NzQxMzc2OWFmMzAzNDRmMTRmMjg0Y2UxZjdlOWQ3NzA0Y2IyZDUiLCJiYW5uZXJzL3YyL2NvbnRlbnQvZGFwcHMvbG9jYWxpemVkX2Rldi9lbi5qc29uIjoiMTZhMzUyYzE0Y2QzYjMwYzVmZTlkMmI4MmY0NWYxMjhmMzUzZDEwYWZmMjExOWQwMDNjMjliNTE2MGIzNDI3YSIsImJhbm5lcnMvdjIvY29udGVudC9kYXBwcy9sb2NhbGl6ZWRfZGV2L2t1Lmpzb24iOiJlMmY1NWEzMzdlNWVhZTM0MzllZTQ4ODgyNDc0MTM3NjlhZjMwMzQ0ZjE0ZjI4NGNlMWY3ZTlkNzcwNGNiMmQ1IiwiY2hhaW5zL2NoYWlucy5qc29uIjoiMTM5OWE3Yjg2NTNhYmY3MWVkOTIxODg3ZGFmODRhN2I1ZTY0ZTcxMTUyMjA0NGFjYmQwMTk0ZWExZjRjYTYxZiIsImNoYWlucy9jaGFpbnNfZGV2Lmpzb24iOiJkNWI4NzhkNjY5NDIzODBmN2JjYWZiOTQ2YzRmYjlkOTkzNDA5NDc3YWFkMTljOTM2MDQ0N2MwNDM0NjNiY2ZjIiwiY2hhaW5zL3Blemt1d2ljaGFpbi9jaGFpbnMuanNvbiI6ImMwOWIyYmU5N2ZjMTRhOTM4ZGE1MDVhZmY2NTMyMzQxM2ExNjA2NzQwZTg1ODkxOTY4OWI5NDdkMmIyZmUxZDMiLCJjaGFpbnMvdHlwZXMvYWx0YWlyLmpzb24iOiIxNjg3ZTkyNGQyMjcxZmQ3NjhjYWI3NWQ0OTYxYTY3YzkwYTAwNTNkMTE4ZjFkOGI1YWFlY2EwM2NjOTUwMTQzIiwiY2hhaW5zL3R5cGVzL2JpZnJvc3QuanNvbiI6ImZmMmVjMjdiYzIyMzhmOTYyMWI5MTExYjExMDI1YTY1NjRiNWY2YWVhMzE4ZjE3NzVhNjAyOTUwNDliNDQ0MGEiLCJjaGFpbnMvdHlwZXMvY2FsYW1hcmkuanNvbiI6ImQ1MTUxZGRhMmQ1OWMyMjM5YTAwNjQ0YjMxMjc0NWQ2NDYyMTE0ZmE2YTQ1ZGQ1NDkxZGY4YjkyNDU4ZTE0NmIiLCJjaGFpbnMvdHlwZXMvZGVmYXVsdC5qc29uIjoiNTYxNjA5M2ZjMWI2NDY5MTZkY2E1YjljMzRjMThjZGRjMDUxMDE1NmMwYjgxNTAwOGMyODA5OGM1ZGYzYmY3MyIsImNoYWlucy90eXBlcy9lZGdld2FyZS5qc29uIjoiMjAwZTg4NTE5ZDJlMjMxOTcwMDVjODkyMzYyMGM1Zjg4NmUxMmY3NDNhYWY2YjEyOTdiNDlhOWRmMGQ2Y2NkNSIsImNoYWlucy90eXBlcy9rYXJ1cmEuanNvbiI6IjdkZTZhMjcwMWRiODQ2MmFmYmIyMmVlMGMyNmU2NGI3MDY2N2YyMTM4MTE3ZWNhMjVhNDJmMWY3Mjk1NjQ3MDQiLCJjaGFpbnMvdHlwZXMva2lsdC5qc29uIjoiYmE2N2M3OGEwNGUwYWI5YTI1ZDcxMWY2YzRjZTFiNTEyMjYyMWEyMDdiYmU1MzI1ZjdhOTE3NjlkMTkwMjMwZSIsImNoYWlucy90eXBlcy9rdXNhbWEuanNvbiI6ImZjYzE2OWRjZDQ5OThkMjM3YTAwZmE4NmI3ODI3YzM1ZjhhZTdlNDFjMDYyNmRmNjRiZmQyM2QyMTI1NGFlNWMiLCJjaGFpbnMvdHlwZXMvbW9vbmJhc2UuanNvbiI6IjFkMjVkZjc0YjNlNDIyZjcxOGYxOTdhZGUwNWNlYTZjMTdhOWM3ZWZiMmIxYjVjOTlhMGRiOWExNzAyYjlhODMiLCJjaGFpbnMvdHlwZXMvbW9vbnJpdmVyLmpzb24iOiJjY2E5OTMzNWFlMjU3MmE2ZjRmYmZhMmFhMjNkZDgxMjIxY2NmNGQ1M2M0YjNkMGE1OGUxZjAwYjRjYjRmM2MxIiwiY2hhaW5zL3R5cGVzL3BhcmFsbGVsLmpzb24iOiJiN2ZiNTIyYWFjNGZjODdkMWYyZWNkMjg4NTVlYjcyZmI3ZTEzM2Q1ZjQyYWEwNGQ1ZTJkYjI5MWUxOTdjOGVmIiwiY2hhaW5zL3R5cGVzL3Blemt1d2kuanNvbiI6IjE0NjZkOGU1NDMwNzM4ZDNmZjE1NWQ2NTdhZDlkNTE0MzUzYzIxYjMyYTBmMjc2MTg0MmE0MDkxNWU3MTJhNDMiLCJjaGFpbnMvdHlwZXMvcG9sa2Fkb3QuanNvbiI6IjNmN2ULine truncated
"signatures": [
{
"keyid": "6066e6a8729c340989e7c150efbd63b1efb9ff1ccaa1e1f38fa015c2f1f9ab5a",
"sig": "EEf5kGNPRK7iu5cbHlfiQ+mxYf2jg6gVmb7lSxkZUi2X1VafOKgXrKJi9ol1w6H2FpizXizgPTKgESdYt0A7Cw=="
"sig": "hciTvg3URWpaKqhUd4MdCf9nyOcA1A48hXbCkpPFKqr9t0Wywe7PC3ftVhdDaj7Le9TrSMk59xY0zyZiNYsDBA=="
}
]
}