tx · EkY5moE8FCU9CuGsFaX4Eaum76PqX5UM78oNkni7ezPh 3Msx4Aq69zWUKy4d1wyKnQ4ofzEDAfv5Ngf: -0.04300000 Waves 2024.09.18 13:10 [3288973] smart account 3Msx4Aq69zWUKy4d1wyKnQ4ofzEDAfv5Ngf > SELF 0.00000000 Waves
{ "type": 13, "id": "EkY5moE8FCU9CuGsFaX4Eaum76PqX5UM78oNkni7ezPh", "fee": 4300000, "feeAssetId": null, "timestamp": 1726654291551, "version": 2, "chainId": 84, "sender": "3Msx4Aq69zWUKy4d1wyKnQ4ofzEDAfv5Ngf", "senderPublicKey": "62i1XasxLi9NJmrEaq9UaHEGfZaonGKUL6EmFaA7gosh", "proofs": [ "2zgpy8Y6VwhcGn9WjSJSc9XidtvCAGCgoYhQZkev8x6hiwXESTWvH165yKqGYBctsC6nNnU1VUNWSRbumiQxKvpJ" ], "script": "base64:CAJACAISBwoFCAgCCAESBwoFCAgCCAESCAoGCAgCAQgBEgYKBAgICAESAwoBAhIAEgMKAQgSBgoECBIBARIECgIIAUoAB0lOVF9NQVgA//////////9/AAVXQVZFUwCAwtcvAAtNSU5fQkFMQU5DRQkAaAIAoJwBBQVXQVZFUwADU0VQAgEsAA9CTE9DS19IQVNIX1NJWkUAIAAUUFVCTElDX0tFWV9IQVNIX1NJWkUAFAAOUk9PVF9IQVNIX1NJWkUAIAAUV0lUSERSQVdfUFJPT0ZTX1NJWkUACgAURVRIX0FERFJFU1NfU1RSX1NJWkUAKAAWTUFYX0NMX1RPX0VMX1RSQU5TRkVSUwAQAAl6ZXJvZXNTdHICgAgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwAAxhbGxNaW5lcnNLZXkCCWFsbE1pbmVycwAObWFpbkNoYWluSWRLZXkCC21haW5DaGFpbklkAA5sYXN0Q2hhaW5JZEtleQILbGFzdENoYWluSWQAF2ZpcnN0VmFsaWRBbHRDaGFpbklkS2V5AhRmaXJzdFZhbGlkQWx0Q2hhaW5JZAAObWluZXJSZXdhcmRLZXkCC21pbmVyUmV3YXJkAApibG9ja01ldGFLAghibG9ja18weAARZmluYWxpemVkQmxvY2tLZXkCDmZpbmFsaXplZEJsb2NrAAp0b2tlbklkS2V5Agd0b2tlbklkABJlbEJyaWRnZUFkZHJlc3NLZXkCD2VsQnJpZGdlQWRkcmVzcwAXbmF0aXZlVHJhbnNmZXJzQ291bnRLZXkCFG5hdGl2ZVRyYW5zZmVyc0NvdW50AQNwYWQBAWkEAXMJAKQDAQUBaQQHJG1hdGNoMAkAsQIBBQFzAwkAAAIAAQUHJG1hdGNoMAkArAICAgcwMDAwMDAwBQFzAwkAAAIAAgUHJG1hdGNoMAkArAICAgYwMDAwMDAFAXMDCQAAAgADBQckbWF0Y2gwCQCsAgICBTAwMDAwBQFzAwkAAAIABAUHJG1hdGNoMAkArAICAgQwMDAwBQFzAwkAAAIABQUHJG1hdGNoMAkArAICAgMwMDAFAXMDCQAAAgAGBQckbWF0Y2gwCQCsAgICAjAwBQFzAwkAAAIABwUHJG1hdGNoMAkArAICAgEwBQFzBQFzARRibG9ja0UyQ1RyYW5zZmVyc0tleQEMYmxvY2tIYXNoSGV4CQCsAgICEmVsVG9DbFRyYW5zZmVyc18weAUMYmxvY2tIYXNoSGV4AQxlcG9jaE1ldGFLZXkBBWVwb2NoCQCsAgICBmVwb2NoXwkBA3BhZAEFBWVwb2NoARRjaGFpbkZpcnN0QmxvY2tJZEtleQEHY2hhaW5JZAkArAICCQCsAgICBWNoYWluCQCkAwEFB2NoYWluSWQCCkZpcnN0QmxvY2sBDGNoYWluTWV0YUtleQEHY2hhaW5JZAkArAICAgZjaGFpbl8JAQNwYWQBBQdjaGFpbklkARJjaGFpbkxhc3RIZWlnaHRLZXkCB2NoYWluSWQFbWluZXIJAKwCAgkArAICCQCsAgICBmNoYWluXwkBA3BhZAEFB2NoYWluSWQCAV8JAKUIAQUFbWluZXIBFGNoYWluRm9ya2VkSGVpZ2h0S2V5AQdjaGFpbklkCQCsAgIJAKwCAgIGY2hhaW5fCQEDcGFkAQUHY2hhaW5JZAIMRm9ya2VkSGVpZ2h0AQ1zdXBwb3J0ZXJzS2V5AQdjaGFpbklkCQCsAgIJAKwCAgIFY2hhaW4JAKQDAQUHY2hhaW5JZAIKU3VwcG9ydGVycwEVbWluZXJSZXdhcmRBZGRyZXNzS2V5AQltaW5lckFkZHIJAKwCAgkArAICAgZtaW5lcl8FCW1pbmVyQWRkcgIOX1Jld2FyZEFkZHJlc3MBCm1pbmVyUGtLZXkBDXJld2FyZEFkZHJlc3MJAKwCAgkArAICAghtaW5lcl8weAUNcmV3YXJkQWRkcmVzcwIDX1BLAQ9taW5lckNoYWluSWRLZXkBBW1pbmVyCQCsAgIJAKwCAgIGbWluZXJfCQClCAEFBW1pbmVyAghfQ2hhaW5JZAAUbmF0aXZlVHJhbnNmZXJzQ291bnQJAQt2YWx1ZU9yRWxzZQIJAJoIAgUEdGhpcwUXbmF0aXZlVHJhbnNmZXJzQ291bnRLZXkAAAERbmF0aXZlVHJhbnNmZXJLZXkBBWluZGV4CQCsAgICD25hdGl2ZVRyYW5zZmVyXwkApAMBBQVpbmRleAEVbWtOYXRpdmVUcmFuc2ZlckVudHJ5AwVpbmRleBBkZXN0RWxBZGRyZXNzSGV4BmFtb3VudAkBC1N0cmluZ0VudHJ5AgkBEW5hdGl2ZVRyYW5zZmVyS2V5AQUFaW5kZXgJAKwCAgkArAICCQCsAgICAjB4BRBkZXN0RWxBZGRyZXNzSGV4BQNTRVAJAKQDAQUGYW1vdW50ARZlbnN1cmVDb3JyZWN0VHJhbnNmZXJzAxByZWZUcmFuc2ZlckluZGV4DXRyYW5zZmVySW5kZXgMZXhwZWN0UmV3YXJkBAxtYXhUcmFuc2ZlcnMDBQxleHBlY3RSZXdhcmQJAGUCBRZNQVhfQ0xfVE9fRUxfVFJBTlNGRVJTAAEFFk1BWF9DTF9UT19FTF9UUkFOU0ZFUlMED2FjdHVhbFRyYW5zZmVycwkAZQIFDXRyYW5zZmVySW5kZXgFEHJlZlRyYW5zZmVySW5kZXgEC2NoZWNrTnVtYmVyAwkAZgIFD2FjdHVhbFRyYW5zZmVycwUMbWF4VHJhbnNmZXJzCQACAQkArAICCQCsAgIJAKwCAgINQWxsb3dlZCBvbmx5IAkApAMBBQxtYXhUcmFuc2ZlcnMCECB0cmFuc2ZlcnMsIGdvdCAJAKQDAQUPYWN0dWFsVHJhbnNmZXJzBgMJAAACBQtjaGVja051bWJlcgULY2hlY2tOdW1iZXIDCQBnAgUNdHJhbnNmZXJJbmRleAUUbmF0aXZlVHJhbnNmZXJzQ291bnQJAAIBCQCsAgIJAKwCAgkArAICAhVBdHRlbXB0IHRvIHRyYW5zZmVyICMJAKQDAQUNdHJhbnNmZXJJbmRleAIXLiBBdmFpbGFibGUgdHJhbnNmZXJzOiAJAKQDAQUUbmF0aXZlVHJhbnNmZXJzQ291bnQGCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAQphbW91bnRHdEVxAwF0BGd0RXEJcXVldWVTaXplAwkAZwIIBQF0BmFtb3VudAUEZ3RFcQYJAAIBCQCsAgIJAKwCAgkArAICCQCsAgIJAKwCAgkArAICAhRUcmFuc2ZlcnJpbmcgYW1vdW50IAkApAMBCAUBdAZhbW91bnQCDiBzaG91bGQgYmUgPj0gCQCkAwEFBGd0RXECEyBmb3IgcXVldWUgc2l6ZSBvZiAJAKQDAQUJcXVldWVTaXplAhcuIFRyYW5zZmVyIG1vcmUgb3Igd2FpdAERZ2VuZXJhdGluZ0JhbGFuY2UBB2FkZHJlc3MICQDvBwEFB2FkZHJlc3MKZ2VuZXJhdGluZwEJY2hhaW5NZXRhAQdjaGFpbklkBAFzCQERQGV4dHJOYXRpdmUoMTA1OCkBCQEMY2hhaW5NZXRhS2V5AQUHY2hhaW5JZAQFaXRlbXMJALUJAgUBcwUDU0VQCQCUCgIJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQVpdGVtcwAACQCRAwIFBWl0ZW1zAAEBEG1rQ2hhaW5NZXRhRW50cnkDB2NoYWluSWQObmV3Q2hhaW5IZWlnaHQMYmxvY2tIYXNoSGV4CQELU3RyaW5nRW50cnkCCQEMY2hhaW5NZXRhS2V5AQUHY2hhaW5JZAkArAICCQCsAgIJAKQDAQUObmV3Q2hhaW5IZWlnaHQFA1NFUAUMYmxvY2tIYXNoSGV4AAttYWluQ2hhaW5JZAkBC3ZhbHVlT3JFbHNlAgkAnwgBBQ5tYWluQ2hhaW5JZEtleQAAAAskdDA1MDI5NTA5NQkBCWNoYWluTWV0YQEFC21haW5DaGFpbklkAA9tYWluQ2hhaW5IZWlnaHQIBQskdDA1MDI5NTA5NQJfMQASbWFpbkNoYWluTGFzdEJsb2NrCAULJHQwNTAyOTUwOTUCXzIBCWVwb2NoTWV0YQEFZXBvY2gEByRtYXRjaDAJAKIIAQkBDGVwb2NoTWV0YUtleQEFBWVwb2NoAwkAAQIFByRtYXRjaDACBlN0cmluZwQBcwUHJG1hdGNoMAQJZnJhZ21lbnRzCQC1CQIFAXMFA1NFUAkAlQoDCQERQGV4dHJOYXRpdmUoMTA2MikBCQCRAwIFCWZyYWdtZW50cwAACQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUJZnJhZ21lbnRzAAEJAJEDAgUJZnJhZ21lbnRzAAIFBHVuaXQACyR0MDUzMjY1NDUwBAckbWF0Y2gwCQEJZXBvY2hNZXRhAQUGaGVpZ2h0AwkAAQIFByRtYXRjaDACFihBZGRyZXNzLCBJbnQsIFN0cmluZykEAW0FByRtYXRjaDAFAW0JAJQKAgUEdW5pdAAAAA50aGlzRXBvY2hNaW5lcggFCyR0MDUzMjY1NDUwAl8xAAx0aGlzRXBvY2hSZWYIBQskdDA1MzI2NTQ1MAJfMgAMYWxsTWluZXJzU3RyCQELdmFsdWVPckVsc2UCCQCiCAEFDGFsbE1pbmVyc0tleQIAAAlhbGxNaW5lcnMEByRtYXRjaDAFDGFsbE1pbmVyc1N0cgMJAAACAgAFByRtYXRjaDAFA25pbAMJAAECBQckbWF0Y2gwAgZTdHJpbmcEA3JhdwUHJG1hdGNoMAkAvAkCBQNyYXcFA1NFUAkAAgECC01hdGNoIGVycm9yAQlibG9ja01ldGEBB2Jsb2NrSWQEBG1ldGEJARFAZXh0ck5hdGl2ZSgxMDU3KQEJAKwCAgUKYmxvY2tNZXRhSwUHYmxvY2tJZAQIbWV0YVNpemUJAMgBAQUEbWV0YQQLYmxvY2tIZWlnaHQJALEJAQUEbWV0YQQKYmxvY2tFcG9jaAkAsgkCBQRtZXRhAAgEC2Jsb2NrUGFyZW50CQDJAQIJAMoBAgUEbWV0YQAQBQ9CTE9DS19IQVNIX1NJWkUEB2NoYWluSWQJALIJAgUEbWV0YQkAZAIAEAUPQkxPQ0tfSEFTSF9TSVpFBApiYXNlT2Zmc2V0CQBkAgAYBQ9CTE9DS19IQVNIX1NJWkUEDnJlbWFpbmluZ0J5dGVzCQBlAgUIbWV0YVNpemUFCmJhc2VPZmZzZXQEFGUyY1RyYW5zZmVyc1Jvb3RIYXNoAwkAZwIFDnJlbWFpbmluZ0J5dGVzBQ5ST09UX0hBU0hfU0laRQkAyQECCQDKAQIFBG1ldGEFCmJhc2VPZmZzZXQFDlJPT1RfSEFTSF9TSVpFAQAEFGxhc3RDMkVUcmFuc2ZlckluZGV4AwMJAAACBQ5yZW1haW5pbmdCeXRlcwAIBgkAZgIFDnJlbWFpbmluZ0J5dGVzBQ5ST09UX0hBU0hfU0laRQkAsgkCBQRtZXRhCQBkAgUKYmFzZU9mZnNldAkAyAEBBRRlMmNUcmFuc2ZlcnNSb290SGFzaAD///////////8BCQCYCgYFC2Jsb2NrSGVpZ2h0BQpibG9ja0Vwb2NoBQtibG9ja1BhcmVudAUHY2hhaW5JZAUUZTJjVHJhbnNmZXJzUm9vdEhhc2gFFGxhc3RDMkVUcmFuc2ZlckluZGV4ARBta0Jsb2NrTWV0YUVudHJ5BgxibG9ja0hhc2hIZXgLYmxvY2tIZWlnaHQOYmxvY2tQYXJlbnRIZXgHY2hhaW5JZBdlMmNUcmFuc2ZlcnNSb290SGFzaEhleBRsYXN0QzJFVHJhbnNmZXJJbmRleAQZZTJjVHJhbnNmZXJzUm9vdEhhc2hCeXRlcwkA3QQBBRdlMmNUcmFuc2ZlcnNSb290SGFzaEhleAQRcm9vdEhhc2hCeXRlc1NpemUJAMgBAQUZZTJjVHJhbnNmZXJzUm9vdEhhc2hCeXRlcwQNY2hlY2tSb290SGFzaAMDCQAAAgURcm9vdEhhc2hCeXRlc1NpemUAAAYJAAACBRFyb290SGFzaEJ5dGVzU2l6ZQUOUk9PVF9IQVNIX1NJWkUGCQACAQkArAICCQCsAgIJAKwCAgIlVHJhbnNmZXJzIHJvb3QgaGFzaCBzaG91bGQgaGF2ZSAwIG9yIAkApAMBBQ5ST09UX0hBU0hfU0laRQIMIGJ5dGVzLCBnb3QgCQCkAwEFEXJvb3RIYXNoQnl0ZXNTaXplAwkAAAIFDWNoZWNrUm9vdEhhc2gFDWNoZWNrUm9vdEhhc2gEDmJsb2NrTWV0YUJ5dGVzCQDLAQIJAMsBAgkAywECCQDLAQIJAMsBAgkAmgMBBQtibG9ja0hlaWdodAkAmgMBBQZoZWlnaHQJAN0EAQUOYmxvY2tQYXJlbnRIZXgJAJoDAQUHY2hhaW5JZAUZZTJjVHJhbnNmZXJzUm9vdEhhc2hCeXRlcwkAmgMBBRRsYXN0QzJFVHJhbnNmZXJJbmRleAkBC0JpbmFyeUVudHJ5AgkArAICBQpibG9ja01ldGFLBQxibG9ja0hhc2hIZXgFDmJsb2NrTWV0YUJ5dGVzCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAQxsYXN0SGVpZ2h0QnkCBW1pbmVyB2NoYWluSWQEByRtYXRjaDAJAJ8IAQkBEmNoYWluTGFzdEhlaWdodEtleQIFB2NoYWluSWQFBW1pbmVyAwkAAQIFByRtYXRjaDACA0ludAQBaAUHJG1hdGNoMAUBaAQJYmxvY2tIYXNoCQERQGV4dHJOYXRpdmUoMTA1OCkBCQCsAgIJAKwCAgkArAICAgVjaGFpbgkApAMBBQdjaGFpbklkAgtMYXN0TWluZWRCeQkApQgBBQVtaW5lcggJAQlibG9ja01ldGEBBQlibG9ja0hhc2gCXzEACyR0MDc0OTA4NDI1BAloaXRTb3VyY2UEByRtYXRjaDAIBQlsYXN0QmxvY2sDdnJmAwkAAQIFByRtYXRjaDACCkJ5dGVWZWN0b3IEA3ZyZgUHJG1hdGNoMAUDdnJmCAUJbGFzdEJsb2NrE2dlbmVyYXRpb25TaWduYXR1cmUKAQxwcm9jZXNzTWluZXICBHByZXYFbWluZXIECyR0MDc3NTc3ODIwBQRwcmV2BAlwcmV2TWluZXIIBQskdDA3NzU3NzgyMAJfMQQQcHJldlRvdGFsQmFsYW5jZQgFCyR0MDc3NTc3ODIwAl8yBAlwcmV2RGVsYXkIBQskdDA3NzU3NzgyMAJfMwQKcHJldk1pbmVycwgFCyR0MDc3NTc3ODIwAl80BAxtaW5lckFkZHJlc3MJARFAZXh0ck5hdGl2ZSgxMDYyKQEFBW1pbmVyBA93YXZlc0dlbkJhbGFuY2UICQDvBwEFDG1pbmVyQWRkcmVzcwpnZW5lcmF0aW5nBAxtaW5lckJhbGFuY2UJARFnZW5lcmF0aW5nQmFsYW5jZQEFDG1pbmVyQWRkcmVzcwMDCQBmAgULTUlOX0JBTEFOQ0UFD3dhdmVzR2VuQmFsYW5jZQYJAGcCAAAFDG1pbmVyQmFsYW5jZQUEcHJldgQJbmV4dERlbGF5CQCFBwIFDG1pbmVyQWRkcmVzcwUMbWluZXJCYWxhbmNlAwkAZgIFCXByZXZEZWxheQUJbmV4dERlbGF5CQCWCgQFBW1pbmVyCQBkAgUQcHJldlRvdGFsQmFsYW5jZQUMbWluZXJCYWxhbmNlBQluZXh0RGVsYXkJAM0IAgUKcHJldk1pbmVycwUFbWluZXIJAJYKBAUJcHJldk1pbmVyCQBkAgUQcHJldlRvdGFsQmFsYW5jZQUMbWluZXJCYWxhbmNlBQlwcmV2RGVsYXkJAM0IAgUKcHJldk1pbmVycwUFbWluZXIKAAIkbAUJYWxsTWluZXJzCgACJHMJAJADAQUCJGwKAAUkYWNjMAkAlgoEAgAAAAUHSU5UX01BWAUDbmlsCgEFJGYwXzECAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAQxwcm9jZXNzTWluZXICBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECFExpc3Qgc2l6ZSBleGNlZWRzIDUwCQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgADAAQABQAGAAcACAAJAAoACwAMAA0ADgAPABAAEQASABMAFAAVABYAFwAYABkAGgAbABwAHQAeAB8AIAAhACIAIwAkACUAJgAnACgAKQAqACsALAAtAC4ALwAwADEAMgARY29tcHV0ZWRHZW5lcmF0b3IIBQskdDA3NDkwODQyNQJfMQAUY29tcHV0ZWRUb3RhbEJhbGFuY2UIBQskdDA3NDkwODQyNQJfMgALJHQwODQyNzg0OTMJAQlibG9ja01ldGEBBRJtYWluQ2hhaW5MYXN0QmxvY2sADG1jbGJJZ25vcmVkMQgFCyR0MDg0Mjc4NDkzAl8xAA5tYWluQ2hhaW5FcG9jaAgFCyR0MDg0Mjc4NDkzAl8yARtjYWxjdWxhdGVGaW5hbGl6ZWRCbG9ja0hhc2gDCGN1ck1pbmVyDGN1clByZXZFcG9jaBBjdXJMYXN0QmxvY2tIYXNoBAtvZmZzZXRzXzEwMAkAvAkCAmQ6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6AgAEC2hhbGZCYWxhbmNlCQBpAgUUY29tcHV0ZWRUb3RhbEJhbGFuY2UAAgoBBHN0ZXACBHByZXYEbmV4dAQLJHQwODg1OTg5MjMFBHByZXYECXRoaXNFcG9jaAgFCyR0MDg4NTk4OTIzAl8xBA5tYXliZVNhZmVFcG9jaAgFCyR0MDg4NTk4OTIzAl8yBAx0b3RhbEJhbGFuY2UIBQskdDA4ODU5ODkyMwJfMwQKcHJldk1pbmVycwgFCyR0MDg4NTk4OTIzAl80BAckbWF0Y2gwBQ5tYXliZVNhZmVFcG9jaAMJAAECBQckbWF0Y2gwAgRVbml0BAskdDA4OTgxOTE0NwMJAAACBQl0aGlzRXBvY2gFBmhlaWdodAkAlQoDBQhjdXJNaW5lcgUMY3VyUHJldkVwb2NoBRBjdXJMYXN0QmxvY2tIYXNoCQEFdmFsdWUBCQEJZXBvY2hNZXRhAQUJdGhpc0Vwb2NoBAVtaW5lcggFCyR0MDg5ODE5MTQ3Al8xBAlwcmV2RXBvY2gIBQskdDA4OTgxOTE0NwJfMgQNbGFzdEJsb2NrSGFzaAgFCyR0MDg5ODE5MTQ3Al8zAwMJAAACBQlwcmV2RXBvY2gAAAYJAGcCCQBlAgUGaGVpZ2h0BQl0aGlzRXBvY2gAZAkAlgoEBQl0aGlzRXBvY2gFDWxhc3RCbG9ja0hhc2gFDHRvdGFsQmFsYW5jZQUJYWxsTWluZXJzBAskdDA5MzAxOTUwMwMJAQ9jb250YWluc0VsZW1lbnQCBQpwcmV2TWluZXJzBQVtaW5lcgkAlAoCBQx0b3RhbEJhbGFuY2UFCnByZXZNaW5lcnMJAJQKAgkAZAIFDHRvdGFsQmFsYW5jZQkBEWdlbmVyYXRpbmdCYWxhbmNlAQUFbWluZXIJAMwIAgUFbWluZXIFCnByZXZNaW5lcnMED25ld1RvdGFsQmFsYW5jZQgFCyR0MDkzMDE5NTAzAl8xBAluZXdNaW5lcnMIBQskdDA5MzAxOTUwMwJfMgMJAGYCBQ9uZXdUb3RhbEJhbGFuY2UFC2hhbGZCYWxhbmNlCQCW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IGFsbG93ZWQgdG8gbWluZSBpbiAJAKQDAQUGaGVpZ2h0AhEgZXBvY2guIEV4cGVjdGVkIAURY29tcHV0ZWRHZW5lcmF0b3IFBHVuaXQBEmlzUmVmZXJlbmNlQ29ycmVjdAIJcmVmZXJlbmNlCWxhc3RCbG9jawMJAAACBQlyZWZlcmVuY2UFCWxhc3RCbG9jawUEdW5pdAkAAgEJAKwCAgkArAICCQCsAgICMEV4cGVjdGVkIGEgcmVmZXJlbmNlIHRvIHRoZSBjaGFpbiBsYXN0IGJsb2NrOiAweAUJbGFzdEJsb2NrAgkuIEdvdDogMHgFCXJlZmVyZW5jZQEPY2hhaW5Jc0luYWN0aXZlAQdjaGFpbklkBAxmaXJzdEJsb2NrSWQJARFAZXh0ck5hdGl2ZSgxMDU4KQEJARRjaGFpbkZpcnN0QmxvY2tJZEtleQEFB2NoYWluSWQEFGZpcnN0VmFsaWRBbHRDaGFpbklkCQELdmFsdWVPckVsc2UCCQCfCAEFF2ZpcnN0VmFsaWRBbHRDaGFpbklkS2V5AAADCQBmAgUUZmlyc3RWYWxpZEFsdENoYWluSWQFB2NoYWluSWQGCQBmAggJAQlibG9ja01ldGEBCQERQGV4dHJOYXRpdmUoMTA1OCkBBRFmaW5hbGl6ZWRCbG9ja0tleQJfMQgJAQlibG9ja01ldGEBBQxmaXJzdEJsb2NrSWQCXzEBDG1pbmVyQ2hhaW5JZAEFbWluZXIJAQt2YWx1ZU9yRWxzZQIJAJ8IAQkBD21pbmVyQ2hhaW5JZEtleQEFBW1pbmVyCQCfCAEJAKwCAgIJY2hhaW5JZE9mCQClCAEFBW1pbmVyAR1lbnN1cmVFeHBlY3RlZE9ySW5hY3RpdmVDaGFpbgMJZ2VuZXJhdG9yD2V4cGVjdGVkQ2hhaW5JZBBjaGVja0hlaWdodEJsb2NrBA9oZWlnaHRJc0NvcnJlY3QEByRtYXRjaDAFEGNoZWNrSGVpZ2h0QmxvY2sDCQABAgUHJG1hdGNoMAIGU3RyaW5nBAlibG9ja0hhc2gFByRtYXRjaDAEFGxhc3RNaW5lZEJsb2NrSGVpZ2h0CQEMbGFzdEhlaWdodEJ5AgUJZ2VuZXJhdG9yBQttYWluQ2hhaW5JZAkAZgIJAGQCCAkBCWJsb2NrTWV0YQEFCWJsb2NrSGFzaAJfMQABBRRsYXN0TWluZWRCbG9ja0hlaWdodAYEByRtYXRjaDAJAQxtaW5lckNoYWluSWQBBQlnZW5lcmF0b3IDCQABAgUHJG1hdGNoMAIDSW50BAljdXJyZW50SWQFByRtYXRjaDADAwkAAAIFCWN1cnJlbnRJZAUPZXhwZWN0ZWRDaGFpbklkBgMJAQ9jaGFpbklzSW5hY3RpdmUBBQljdXJyZW50SWQFD2hlaWdodElzQ29ycmVjdAcFBHVuaXQJAAIBCQCsAgICHG1pbmVyIGlzIG1pbmluZyBvdGhlciBjaGFpbiAJAKQDAQUJY3VycmVudElkBQR1bml0AAxoZWlnaHRTdHJpbmcJAKwCAgILIGF0IGhlaWdodCAJAKQDAQUGaGVpZ2h0AQV2cmZBdAEGaGVpZ2h0CQETdmFsdWVPckVycm9yTWVzc2FnZQIICQETdmFsdWVPckVycm9yTWVzc2FnZQIJAO0HAQUGaGVpZ2h0CQCsAgICG2xhc3QgYmxvY2sgaXMgbm90IGF2YWlsYWJsZQUMaGVpZ2h0U3RyaW5nA3ZyZgkArAICAhRWUkYgaXMgbm90IGF2YWlsYWJsZQUMaGVpZ2h0U3RyaW5nARJlbnN1cmVDb3JyZWN0RXBvY2gBC2V4cGVjdGVkVlJGBAlhY3R1YWxWUkYJAQV2cmZBdAEFBmhlaWdodAMJAAACBQtleHBlY3RlZFZSRgUJYWN0dWFsVlJGBQR1bml0CQACAQkArAICCQCsAgIJAKwCAgkArAICAg1FeHBlY3RlZCBWUkYgCQDYBAEFC2V4cGVjdGVkVlJGAhcgZG9lcyBub3QgbWF0Y2ggYWN0dWFsIAkA2AQBBQlhY3R1YWxWUkYFDGhlaWdodFN0cmluZwEMYWRkU3VwcG9ydGVyAgdjaGFpbklkCWdlbmVyYXRvcgQNc3VwcG9ydGVyc1N0cgkBEUBleHRyTmF0aXZlKDEwNTgpAQkBDXN1cHBvcnRlcnNLZXkBBQdjaGFpbklkBApzdXBwb3J0ZXJzCQC8CQIFDXN1cHBvcnRlcnNTdHIFA1NFUAMJAQ9jb250YWluc0VsZW1lbnQCBQpzdXBwb3J0ZXJzCQClCAEFCWdlbmVyYXRvcgUDbmlsCQDMCAIJAQtTdHJpbmdFbnRyeQIJAQ1zdXBwb3J0ZXJzS2V5AQUHY2hhaW5JZAkArAICCQCsAgIFDXN1cHBvcnRlcnNTdHIFA1NFUAkApQgBBQlnZW5lcmF0b3IFA25pbAEJc2V0T3JGYWlsAgVmbGFncwVpbmRleAMJAGYCAAAFBWluZGV4CQACAQkArAICAiJDYW4ndCB3aXRoZHJhdyBhdCBuZWdhdGl2ZSBpbmRleDogCQCkAwEFBWluZGV4BAlmbGFnc1NpemUJALECAQUFZmxhZ3MDCQBnAgUFaW5kZXgFCWZsYWdzU2l6ZQQJYWRkWmVyb2VzCQBlAgUFaW5kZXgFCWZsYWdzU2l6ZQMJAGYCBQlhZGRaZXJvZXMJALECAQUJemVyb2VzU3RyCQACAQkArAICCQCsAgICCkNhbid0IGFkZCAJAKQDAQUJYWRkWmVyb2VzAiUgZW1wdHkgZmxhZ3MuIENvbnRhY3Qgd2l0aCBkZXZlbG9wZXJzCQCsAgIJAKwCAgUFZmxhZ3MJAK8CAgUJemVyb2VzU3RyBQlhZGRaZXJvZXMCATEEBHRhaWwJALACAgUFZmxhZ3MFBWluZGV4BAdhdEluZGV4CQCvAgIFBHRhaWwAAQMJAAACBQdhdEluZGV4AgEwCQCsAgIJAKwCAgkArwICBQVmbGFncwUFaW5kZXgCATEJALACAgUEdGFpbAABCQACAQkArAICCQCsAgICClRyYW5zZmVyICMJAKQDAQUFaW5kZXgCFyBoYXMgYmVlbiBhbHJlYWR5IHRha2VuARF2YWxpZGF0ZUJsb2NrSGFzaAEGaGV4U3RyBAxkZWNvZGVkQnl0ZXMJAN0EAQUGaGV4U3RyAwkBAiE9AgkAyAEBBQxkZWNvZGVkQnl0ZXMFD0JMT0NLX0hBU0hfU0laRQkAAgECF2ludmFsaWQgYmxvY2sgaWQgbGVuZ3RoBQZoZXhTdHIBHWdldFVwZGF0ZUZpbmFsaXplZEJsb2NrQWN0aW9uAwZjYWxsZXIPbmV3QmxvY2tIYXNoSGV4CXByZXZFcG9jaAQXY3VyRmluYWxpemVkQmxvY2tIZWlnaHQICQEJYmxvY2tNZXRhAQkBEUBleHRyTmF0aXZlKDEwNTgpAQURZmluYWxpemVkQmxvY2tLZXkCXzEEFW5ld0ZpbmFsaXplZEJsb2NrSGFzaAkBG2NhbGN1bGF0ZUZpbmFsaXplZEJsb2NrSGFzaAMFBmNhbGxlcgUJcHJldkVwb2NoBQ9uZXdCbG9ja0hhc2hIZXgDAwkAAAIFFW5ld0ZpbmFsaXplZEJsb2NrSGFzaAUPbmV3QmxvY2tIYXNoSGV4BgkAZgIICQEJYmxvY2tNZXRhAQUVbmV3RmluYWxpemVkQmxvY2tIYXNoAl8xBRdjdXJGaW5hbGl6ZWRCbG9ja0hlaWdodAkAzAgCCQELU3RyaW5nRW50cnkCBRFmaW5hbGl6ZWRCbG9ja0tleQUVbmV3RmluYWxpemVkQmxvY2tIYXNoBQNuaWwFA25pbAkBaQEPZXh0ZW5kTWFpbkNoYWluBQxibG9ja0hhc2hIZXgMcmVmZXJlbmNlSGV4A3ZyZhdlMmNUcmFuc2ZlcnNSb290SGFzaEhleBRsYXN0QzJFVHJhbnNmZXJJbmRleAQOY2hlY2tCbG9ja0hhc2gJARF2YWxpZGF0ZUJsb2NrSGFzaAEFDGJsb2NrSGFzaEhleAMJAAACBQ5jaGVja0Jsb2NrSGFzaAUOY2hlY2tCbG9ja0hhc2gECmNoZWNrRXBvY2gJARJlbnN1cmVDb3JyZWN0RXBvY2gBBQN2cmYDCQAAAgUKY2hlY2tFcG9jaAUKY2hlY2tFcG9jaAQKY2hlY2tDaGFpbgkBHWVuc3VyZUV4cGVjdGVkT3JJbmFjdGl2ZUNoYWluAwgFAWkMb3JpZ2luQ2FsbGVyBQttYWluQ2hhaW5JZAUEdW5pdAMJAAACBQpjaGVja0NoYWluBQpjaGVja0NoYWluBA5jaGVja1JlZmVyZW5jZQkBEmlzUmVmZXJlbmNlQ29ycmVjdAIFDHJlZmVyZW5jZUhleAUSbWFpbkNoYWluTGFzdEJsb2NrAwkAAAIFDmNoZWNrUmVmZXJlbmNlBQ5jaGVja1JlZmVyZW5jZQQOY2hlY2tUcmFuc2ZlcnMJARZlbnN1cmVDb3JyZWN0VHJhbnNmZXJzAwgJAQlibG9ja01ldGEBBQxyZWZlcmVuY2VIZXgCXzYFFGxhc3RDMkVUcmFuc2ZlckluZGV4BgMJAAACBQ5jaGVja1RyYW5zZmVycwUOY2hlY2tUcmFuc2ZlcnMEDXRoaXNFcG9jaE1ldGEEByRtYXRjaDAJAQllcG9jaE1ldGEBBQZoZWlnaHQDCQABAgUHJG1hdGNoMAIEVW5pdAkBC1N0cmluZ0VudHJ5AgkBDGVwb2NoTWV0YUtleQEFBmhlaWdodAkArAICCQCsAgIJAKwCAgkArAICCQClCAEIBQFpDG9yaWdpbkNhbGxlcgUDU0VQCQCkAwEFDm1haW5DaGFpbkVwb2NoBQNTRVAFDGJsb2NrSGFzaEhleAQFb3RoZXIFByRtYXRjaDAJAAIBAhVFcG9jaCBhbHJlYWR5IHN0YXJ0ZWQDCQAAAgUNdGhpc0Vwb2NoTWV0YQUNdGhpc0Vwb2NoTWV0YQQOY2hlY2tHZW5lcmF0b3IJARFlbnN1cmVNaW5pbmdFcG9jaAEIBQFpDG9yaWdpbkNhbGxlcgMJAAACBQ5jaGVja0dlbmVyYXRvcgUOY2hlY2tHZW5lcmF0b3IEFHVwZGF0ZUZpbmFsaXplZEJsb2NrCQEdZ2V0VXBkYXRlRmluYWxpemVkQmxvY2tBY3Rpb24DCAUBaQxvcmlnaW5DYWxsZXIFDGJsb2NrSGFzaEhleAUObWFpbkNoYWluRXBvY2gEDm5ld0NoYWluSGVpZ2h0CQBkAgUPbWFpbkNoYWluSGVpZ2h0AAEJAM4IAgkAzAgCCQEQbWtCbG9ja01ldGFFbnRyeQYFDGJsb2NrSGFzaEhleAUObmV3Q2hhaW5IZWlnaHQFEm1haW5DaGFpbkxhc3RCbG9jawULbWFpbkNoYWluSWQFF2UyY1RyYW5zZmVyc1Jvb3RIYXNoSGV4BRRsYXN0QzJFVHJhbnNmZXJJbmRleAkAzAgCCQEQbWtDaGFpbk1ldGFFbnRyeQMFC21haW5DaGFpbklkBQ5uZXdDaGFpbkhlaWdodAUMYmxvY2tIYXNoSGV4CQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEPbWluZXJDaGFpbklkS2V5AQgFAWkMb3JpZ2luQ2FsbGVyBQttYWluQ2hhaW5JZAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBEmNoYWluTGFzdEhlaWdodEtleQIFC21haW5DaGFpbklkCAUBaQxvcmlnaW5DYWxsZXIFDm5ld0NoYWluSGVpZ2h0CQDMCAIFDXRoaXNFcG9jaE1ldGEFA25pbAUUdXBkYXRlRmluYWxpemVkQmxvY2sJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4BaQENc3RhcnRBbHRDaGFpbgUMYmxvY2tIYXNoSGV4DHJlZmVyZW5jZUhleAN2cmYXZTJjVHJhbnNmZXJzUm9vdEhhc2hIZXgUbGFzdEMyRVRyYW5zZmVySW5kZXgEDmNoZWNrQmxvY2tIYXNoCQERdmFsaWRhdGVCbG9ja0hhc2gBBQxibG9ja0hhc2hIZXgDCQAAAgUOY2hlY2tCbG9ja0hhc2gFDmNoZWNrQmxvY2tIYXNoBApjaGVja0Vwb2NoCQESZW5zdXJlQ29ycmVjdEVwb2NoAQUDdnJmAwkAAAIFCmNoZWNrRXBvY2gFCmNoZWNrRXBvY2gEDSR0MDE2MTMyMTYyNDUJAQlibG9ja01ldGEBBQxyZWZlcmVuY2VIZXgEDnJlZkNoYWluSGVpZ2h0CAUNJHQwMTYxMzIxNjI0NQJfMQQIcmVmRXBvY2gIBQ0kdDAxNjEzMjE2MjQ1Al8yBAtyZWZJZ25vcmVkMQgFDSR0MDE2MTMyMTYyNDUCXzMEC3JlZklnbm9yZWQyCAUNJHQwMTYxMzIxNjI0NQJfNAQLcmVmSWdub3JlZDMIBQ0kdDAxNjEzMjE2MjQ1Al81BBByZWZUcmFuc2ZlckluZGV4CAUNJHQwMTYxMzIxNjI0NQJfNgQOZmluYWxpemVkRXBvY2gICQEJYmxvY2tNZXRhAQkBEUBleHRyTmF0aXZlKDEwNTgpAQURZmluYWxpemVkQmxvY2tLZXkCXzIECGVwb2NoUmVmAwkAZwIFCHJlZkVwb2NoBQ5maW5hbGl6ZWRFcG9jaAUIcmVmRXBvY2gJAAIBCQCsAgIJAKwCAgkArAICCQCsAgICI0NhbiBub3Qgc3RhcnQgYWx0IGNoYWluIGZyb20gZXBvY2ggCQCkAwEFCHJlZkVwb2NoAggsIGVwb2NoIAkApAMBBQ5maW5hbGl6ZWRFcG9jaAINIGlzIGZpbmFsaXplZAQKY2hlY2tDaGFpbgkBHWVuc3VyZUV4cGVjdGVkT3JJbmFjdGl2ZUNoYWluAwgFAWkMb3JpZ2luQ2FsbGVyBQttYWluQ2hhaW5JZAUMcmVmZXJlbmNlSGV4AwkAAAIFCmNoZWNrQ2hhaW4FCmNoZWNrQ2hhaW4EDmNoZWNrVHJhbnNmZXJzCQEWZW5zdXJlQ29ycmVjdFRyYW5zZmVycwMFEHJlZlRyYW5zZmVySW5kZXgFFGxhc3RDMkVUcmFuc2ZlckluZGV4BgMJAAACBQ5jaGVja1RyYW5zZmVycwUOY2hlY2tUcmFuc2ZlcnMECm5ld0NoYWluSWQJAGQCCQELdmFsdWVPckVsc2UCCQCfCAEFDmxhc3RDaGFpbklkS2V5AAAAAQQObmV3Q2hhaW5IZWlnaHQJAGQCBQ5yZWZDaGFpbkhlaWdodAABBA10aGlzRXBvY2hNZXRhBAckbWF0Y2gwCQEJZXBvY2hNZXRhAQUGaGVpZ2h0AwkAAQIFByRtYXRjaDACBFVuaXQJAQtTdHJpbmdFbnRyeQIJAQxlcG9jaE1ldGFLZXkBBQZoZWlnaHQJAKwCAgkArAICCQCsAgIJAKwCAgkApQgBCAUBaQxvcmlnaW5DYWxsZXIFA1NFUAkApAMBBQhlcG9jaFJlZgUDU0VQBQxibG9ja0hhc2hIZXgEBW90aGVyBQckbWF0Y2gwCQACAQIVRXBvY2ggYWxyZWFkeSBzdGFydGVkBA5jaGVja0dlbmVyYXRvcgkBEWVuc3VyZU1pbmluZ0Vwb2NoAQgFAWkMb3JpZ2luQ2FsbGVyAwkAAAIFDmNoZWNrR2VuZXJhdG9yBQ5jaGVja0dlbmVyYXRvcgkAzAgCBQ10aGlzRXBvY2hNZXRhCQDMCAIJARBta0Jsb2NrTWV0YUVudHJ5BgUMYmxvY2tIYXNoSGV4BQ5uZXdDaGFpbkhlaWdodAUMcmVmZXJlbmNlSGV4BQpuZXdDaGFpbklkBRdlMmNUcmFuc2ZlcnNSb290SGFzaEhleAUUbGFzdEMyRVRyYW5zZmVySW5kZXgJAMwIAgkBC1N0cmluZ0VudHJ5AgkBFGNoYWluRmlyc3RCbG9ja0lkS2V5AQUKbmV3Q2hhaW5JZAUMYmxvY2tIYXNoSGV4CQDMCAIJARBta0NoYWluTWV0YUVudHJ5AwUKbmV3Q2hhaW5JZAUObmV3Q2hhaW5IZWlnaHQFDGJsb2NrSGFzaEhleAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBD21pbmVyQ2hhaW5JZEtleQEIBQFpDG9yaWdpbkNhbGxlcgUKbmV3Q2hhaW5JZAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBEmNoYWluTGFzdEhlaWdodEtleQIFCm5ld0NoYWluSWQIBQFpDG9yaWdpbkNhbGxlcgUObmV3Q2hhaW5IZWlnaHQJAMwIAgkBDEludGVnZXJFbnRyeQIJARJjaGFpbkxhc3RIZWlnaHRLZXkCBQttYWluQ2hhaW5JZAgFAWkMb3JpZ2luQ2FsbGVyBQ5uZXdDaGFpbkhlaWdodAkAzAgCCQELU3RyaW5nRW50cnkCCQENc3VwcG9ydGVyc0tleQEFCm5ld0NoYWluSWQJAKUIAQgFAWkMb3JpZ2luQ2FsbGVyCQDMCAIJAQxJbnRlZ2VyRW50cnkCBQ5sYXN0Q2hhaW5JZEtleQUKbmV3Q2hhaW5JZAUDbmlsCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBDmV4dGVuZEFsdENoYWluBgxibG9ja0hhc2hIZXgMcmVmZXJlbmNlSGV4A3ZyZgdjaGFpbklkF2UyY1RyYW5zZmVyc1Jvb3RIYXNoSGV4FGxhc3RDMkVUcmFuc2ZlckluZGV4BA5jaGVja0Jsb2NrSGFzaAkBEXZhbGlkYXRlQmxvY2tIYXNoAQUMYmxvY2tIYXNoSGV4AwkAAAIFDmNoZWNrQmxvY2tIYXNoBQ5jaGVja0Jsb2NrSGFzaAQKY2hlY2tFcG9jaAkBEmVuc3VyZUNvcnJlY3RFcG9jaAEFA3ZyZgMJAAACBQpjaGVja0Vwb2NoBQpjaGVja0Vwb2NoBBNjaGFpbkZpcnN0QmxvY2tNZXRhCQEJYmxvY2tNZXRhAQkBEUBleHRyTmF0aXZlKDEwNTgpAQkBFGNoYWluRmlyc3RCbG9ja0lkS2V5AQUHY2hhaW5JZAQKY2hlY2tDaGFpbgkBHWVuc3VyZUV4cGVjdGVkT3JJbmFjdGl2ZUNoYWluAwgFAWkMb3JpZ2luQ2FsbGVyBQdjaGFpbklkCQDcBAEIBRNjaGFpbkZpcnN0QmxvY2tNZXRhAl8zAwkAAAIFCmNoZWNrQ2hhaW4FCmNoZWNrQ2hhaW4EDSR0MDE4NTU4MTg2MTIJAQljaGFpbk1ldGEBBQdjaGFpbklkBAtjaGFpbkhlaWdodAgFDSR0MDE4NTU4MTg2MTICXzEEDmNoYWluTGFzdEJsb2NrCAUNJHQwMTg1NTgxODYxMgJfMgQOY2hlY2tSZWZlcmVuY2UJARJpc1JlZmVyZW5jZUNvcnJlY3QCBQxyZWZlcmVuY2VIZXgFDmNoYWluTGFzdEJsb2NrAwkAAAIFDmNoZWNrUmVmZXJlbmNlBQ5jaGVja1JlZmVyZW5jZQQOY2hlY2tUcmFuc2ZlcnMJARZlbnN1cmVDb3JyZWN0VHJhbnNmZXJzAwgJAQlibG9ja01ldGEBBQxyZWZlcmVuY2VIZXgCXzYFFGxhc3RDMkVUcmFuc2ZlckluZGV4BgMJAAACBQ5jaGVja1RyYW5zZmVycwUOY2hlY2tUcmFuc2ZlcnMEDm5ld0NoYWluSGVpZ2h0CQBkAgULY2hhaW5IZWlnaHQAAQQJcHJldkVwb2NoCAkBCWJsb2NrTWV0YQEFDHJlZmVyZW5jZUhleAJfMgQTdXBkYXRlTWFpbkNoYWluRGF0YQMJAGYCCQERc3VwcG9ydGluZ0JhbGFuY2UBBQdjaGFpbklkCQBpAgUUY29tcHV0ZWRUb3RhbEJhbGFuY2UAAgQLbGFzdENoYWluSWQJAQt2YWx1ZU9yRWxzZQIJAJ8IAQUObGFzdENoYWluSWRLZXkAAAQUdXBkYXRlRmluYWxpemVkQmxvY2sJAR1nZXRVcGRhdGVGaW5hbGl6ZWRCbG9ja0FjdGlvbgMIBQFpDG9yaWdpbkNhbGxlcgUMYmxvY2tIYXNoSGV4BQlwcmV2RXBvY2gJAM4IAgkAzAgCCQEMSW50ZWdlckVudHJ5AgkBFGNoYWluRm9ya2VkSGVpZ2h0S2V5AQULbWFpbkNoYWluSWQIBRNjaGFpbkZpcnN0QmxvY2tNZXRhAl8xCQDMCAIJAQxJbnRlZ2VyRW50cnkCBQ5tYWluQ2hhaW5JZEtleQUHY2hhaW5JZAkAzAgCCQEMSW50ZWdlckVudHJ5AgUXZmlyc3RWYWxpZEFsdENoYWluSWRLZXkJAGQCBQtsYXN0Q2hhaW5JZAABBQNuaWwFFHVwZGF0ZUZpbmFsaXplZEJsb2NrBQNuaWwEDXRoaXNFcG9jaE1ldGEEByRtYXRjaDAJAQllcG9jaE1ldGEBBQZoZWlnaHQDCQABAgUHJG1hdGNoMAIEVW5pdAkBC1N0cmluZ0VudHJ5AgkBDGVwb2NoTWV0YUtleQEFBmhlaWdodAkArAICCQCsAgIJAKwCAgkArAICCQClCAEIBQFpDG9yaWdpbkNhbGxlcgUDU0VQCQCkAwEFCXByZXZFcG9jaAUDU0VQBQxibG9ja0hhc2hIZXgEBW90aGVyBQckbWF0Y2gwCQACAQIVRXBvY2ggYWxyZWFkeSBzdGFydGVkAwkAAAIFDXRoaXNFcG9jaE1ldGEFDXRoaXNFcG9jaE1ldGEEDmNoZWNrR2VuZXJhdG9yCQERZW5zdXJlTWluaW5nRXBvY2gBCAUBaQxvcmlnaW5DYWxsZXIDCQAAAgUOY2hlY2tHZW5lcmF0b3IFDmNoZWNrR2VuZXJhdG9yBB11cGRhdGVNYWluQ2hhaW5MYXN0TWluZWRCbG9jawMDCQAAAgUTdXBkYXRlTWFpbkNoYWluRGF0YQUDbmlsCQECIT0CCQELdmFsdWVPckVsc2UCCQEMbWluZXJDaGFpbklkAQgFAWkMb3JpZ2luQ2FsbGVyAAAFB2NoYWluSWQHCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQESY2hhaW5MYXN0SGVpZ2h0S2V5AgULbWFpbkNoYWluSWQIBQFpDG9yaWdpbkNhbGxlcggFE2NoYWluRmlyc3RCbG9ja01ldGECXzEFA25pbAUDbmlsCQDOCAIJAM4IAgkAzggCCQDMCAIJARBta0Jsb2NrTWV0YUVudHJ5BgUMYmxvY2tIYXNoSGV4BQ5uZXdDaGFpbkhlaWdodAUMcmVmZXJlbmNlSGV4BQdjaGFpbklkBRdlMmNUcmFuc2ZlcnNSb290SGFzaEhleAUUbGFzdEMyRVRyYW5zZmVySW5kZXgJAMwIAgkBEG1rQ2hhaW5NZXRhRW50cnkDBQdjaGFpbklkBQ5uZXdDaGFpbkhlaWdodAUMYmxvY2tIYXNoSGV4CQDMCAIFDXRoaXNFcG9jaE1ldGEJAMwIAgkBDEludGVnZXJFbnRyeQIJAQ9taW5lckNoYWluSWRLZXkBCAUBaQxvcmlnaW5DYWxsZXIFB2NoYWluSWQJAMwIAgkBDEludGVnZXJFbnRyeQIJARJjaGFpbkxhc3RIZWlnaHRLZXkCBQdjaGFpbklkCAUBaQxvcmlnaW5DYWxsZXIFDm5ld0NoYWluSGVpZ2h0BQNuaWwFE3VwZGF0ZU1haW5DaGFpbkRhdGEJAQxhZGRTdXBwb3J0ZXICBQdjaGFpbklkCAUBaQxvcmlnaW5DYWxsZXIFHXVwZGF0ZU1haW5DaGFpbkxhc3RNaW5lZEJsb2NrCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBC2FwcGVuZEJsb2NrBAxibG9ja0hhc2hIZXgMcmVmZXJlbmNlSGV4F2UyY1RyYW5zZmVyc1Jvb3RIYXNoSGV4FGxhc3RDMkVUcmFuc2ZlckluZGV4BAtjaGVja0NhbGxlcgMJAAACBQ50aGlzRXBvY2hNaW5lcggFAWkMb3JpZ2luQ2FsbGVyBgQHJG1hdGNoMAUOdGhpc0Vwb2NoTWluZXIDCQABAgUHJG1hdGNoMAIHQWRkcmVzcwQKZXBvY2hNaW5lcgUHJG1hdGNoMAkAAgEJAKwCAgI5bm90IGFsbG93ZWQgdG8gZm9yZ2UgYmxvY2tzIGluIHRoaXMgZXBvY2gsIGV4cGVjdGVkIGZyb20gCQClCAEFCmVwb2NoTWluZXIJAAIBAkBub3QgYWxsb3dlZCB0byBmb3JnZSBibG9ja3MgaW4gdGhpcyBlcG9jaCwgZXBvY2ggbWluZXIgaXMgYWJzZW50AwkAAAIFC2NoZWNrQ2FsbGVyBQtjaGVja0NhbGxlcgQHY2hhaW5JZAkBC3ZhbHVlT3JFbHNlAgkBDG1pbmVyQ2hhaW5JZAEIBQFpDG9yaWdpbkNhbGxlcgULbWFpbkNoYWluSWQEDSR0MDIwOTYxMjEwMTIJAQljaGFpbk1ldGEBBQdjaGFpbklkBAtjaGFpbkhlaWdodAgFDSR0MDIwOTYxMjEwMTICXzEEC2xhc3RCbG9ja0lkCAUNJHQwMjA5NjEyMTAxMgJfMgQOY2hlY2tSZWZlcmVuY2UJARJpc1JlZmVyZW5jZUNvcnJlY3QCBQxyZWZlcmVuY2VIZXgFC2xhc3RCbG9ja0lkAwkAAAIFDmNoZWNrUmVmZXJlbmNlBQ5jaGVja1JlZmVyZW5jZQQOY2hlY2tUcmFuc2ZlcnMJARZlbnN1cmVDb3JyZWN0VHJhbnNmZXJzAwgJAQlibG9ja01ldGEBBQxyZWZlcmVuY2VIZXgCXzYFFGxhc3RDMkVUcmFuc2ZlckluZGV4BwMJAAACBQ5jaGVja1RyYW5zZmVycwUOY2hlY2tUcmFuc2ZlcnMEDm5ld0NoYWluSGVpZ2h0CQBkAgULY2hhaW5IZWlnaHQAAQQOY2hlY2tCbG9ja0hhc2gJARF2YWxpZGF0ZUJsb2NrSGFzaAEFDGJsb2NrSGFzaEhleAMJAAACBQ5jaGVja0Jsb2NrSGFzaAUOY2hlY2tCbG9ja0hhc2gJAMwIAgkBEG1rQmxvY2tNZXRhRW50cnkGBQxibG9ja0hhc2hIZXgFDm5ld0NoYWluSGVpZ2h0BQtsYXN0QmxvY2tJZAUHY2hhaW5JZAUXZTJjVHJhbnNmZXJzUm9vdEhhc2hIZXgFFGxhc3RDMkVUcmFuc2ZlckluZGV4CQDMCAIJAQxJbnRlZ2VyRW50cnkCCQESY2hhaW5MYXN0SGVpZ2h0S2V5AgUHY2hhaW5JZAgFAWkMb3JpZ2luQ2FsbGVyBQ5uZXdDaGFpbkhlaWdodAkAzAgCCQEQbWtDaGFpbk1ldGFFbnRyeQMFB2NoYWluSWQFDm5ld0NoYWluSGVpZ2h0BQxibG9ja0hhc2hIZXgJAMwIAgkBC1N0cmluZ0VudHJ5AgkBDGVwb2NoTWV0YUtleQEFBmhlaWdodAkArAICCQCsAgIJAKwCAgkArAICCQClCAEJAQV2YWx1ZQEFDnRoaXNFcG9jaE1pbmVyBQNTRVAJAKQDAQUMdGhpc0Vwb2NoUmVmBQNTRVAFDGJsb2NrSGFzaEhleAUDbmlsCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBBGpvaW4BDXJld2FyZEFkZHJlc3MKAR1lbnN1cmVOb3RPdmVycmlkZU90aGVyTWluZXJQawEMZWxBZGRyZXNzSGV4BAckbWF0Y2gwCQChCAEJAQptaW5lclBrS2V5AQUMZWxBZGRyZXNzSGV4AwkAAQIFByRtYXRjaDACCkJ5dGVWZWN0b3IEAnBrBQckbWF0Y2gwAwkAAAIFAnBrCAUBaRVvcmlnaW5DYWxsZXJQdWJsaWNLZXkFBHVuaXQJAAIBCQCsAgIJAKwCAgkArAICAhFFTCBtaW5lciBhZGRyZXNzIAUMZWxBZGRyZXNzSGV4AhggaXMgYWxyZWFkeSBsaW5rZWQgd2l0aCAJANgEAQUCcGsFBHVuaXQDCQEBIQEJAQ9pc0NvbnRyYWN0U2V0dXAACQACAQIfVGhlIGNvbnRyYWN0IGhhcyBub3QgeWV0IHNldCB1cAMJAGYCBQtNSU5fQkFMQU5DRQgJAO8HAQgFAWkMb3JpZ2luQ2FsbGVyCmdlbmVyYXRpbmcJAAIBCQCsAgIJAKwCAgkArAICAiFJbnN1ZmZpY2llbnQgZ2VuZXJhdGluZyBiYWxhbmNlOiAJAKQDAQgJAO8HAQgFAWkMb3JpZ2luQ2FsbGVyCmdlbmVyYXRpbmcCDC4gUmVxdWlyZWQ6IAkApAMBBQtNSU5fQkFMQU5DRQMJAQIhPQIJAMgBAQUNcmV3YXJkQWRkcmVzcwAUCQACAQIlcmV3YXJkQWRkcmVzcyBzaG91bGQgYmUgYW4gTDIgYWRkcmVzcwMJAGcCCQCQAwEFCWFsbE1pbmVycwAyCQACAQIPdG9vIG1hbnkgbWluZXJzCgEOY2hlY2tFeGlzdGVuY2UCBmV4aXN0cwVtaW5lcgMFBmV4aXN0cwYJAAACBQVtaW5lcgkApQgBCAUBaQxvcmlnaW5DYWxsZXIEDWFscmVhZHlFeGlzdHMKAAIkbAUJYWxsTWluZXJzCgACJHMJAJADAQUCJGwKAAUkYWNjMAcKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBDmNoZWNrRXhpc3RlbmNlAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhRMaXN0IHNpemUgZXhjZWVkcyA1MAkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIDBQ1hbHJlYWR5RXhpc3RzBQNuaWwECG5ld01pbmVyCQClCAEIBQFpDG9yaWdpbkNhbGxlcgQQcmV3YXJkQWRkcmVzc0hleAkA3AQBBQ1yZXdhcmRBZGRyZXNzBAVjaGVjawkBHWVuc3VyZU5vdE92ZXJyaWRlT3RoZXJNaW5lclBrAQUQcmV3YXJkQWRkcmVzc0hleAMJAAACBQVjaGVjawUFY2hlY2sECW5ld01pbmVycwMJAAACCQCQAwEFCWFsbE1pbmVycwAABQhuZXdNaW5lcgkArAICCQCsAgIFDGFsbE1pbmVyc1N0cgUDU0VQBQhuZXdNaW5lcgQZZGVsZXRlUHJldlJld2FyZEFkZHJlc3NQawQHJG1hdGNoMAkAoggBCQEVbWluZXJSZXdhcmRBZGRyZXNzS2V5AQUIbmV3TWluZXIDCQABAgUHJG1hdGNoMAIGU3RyaW5nBAtwcmV2QWRkcmVzcwUHJG1hdGNoMAMJAAACBQtwcmV2QWRkcmVzcwkA3AQBBQ1yZXdhcmRBZGRyZXNzBQNuaWwJAMwIAgkBC0RlbGV0ZUVudHJ5AQkBCm1pbmVyUGtLZXkBBQtwcmV2QWRkcmVzcwUDbmlsBQNuaWwJAM4IAgkAzAgCCQELU3RyaW5nRW50cnkCBQxhbGxNaW5lcnNLZXkFCW5ld01pbmVycwkAzAgCCQELU3RyaW5nRW50cnkCCQEVbWluZXJSZXdhcmRBZGRyZXNzS2V5AQUIbmV3TWluZXIJAKwCAgICMHgFEHJld2FyZEFkZHJlc3NIZXgJAMwIAgkBC0JpbmFyeUVudHJ5AgkBCm1pbmVyUGtLZXkBBRByZXdhcmRBZGRyZXNzSGV4CAUBaRVvcmlnaW5DYWxsZXJQdWJsaWNLZXkFA25pbAUZZGVsZXRlUHJldlJld2FyZEFkZHJlc3NQawkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgFpAQVsZWF2ZQAEDGxlYXZpbmdNaW5lcgkApQgBCAUBaQxvcmlnaW5DYWxsZXIKARBza2lwTGVhdmluZ01pbmVyAgNhY2MFbWluZXIDCQAAAgUFbWluZXIFDGxlYXZpbmdNaW5lcgUDYWNjCQDNCAIFA2FjYwUFbWluZXIED3JlbWFpbmluZ01pbmVycwoAAiRsBQlhbGxNaW5lcnMKAAIkcwkAkAMBBQIkbAoABSRhY2MwBQNuaWwKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBEHNraXBMZWF2aW5nTWluZXICBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECFExpc3Qgc2l6ZSBleGNlZWRzIDUwCQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgADAAQABQAGAAcACAAJAAoACwAMAA0ADgAPABAAEQASABMAFAAVABYAFwAYABkAGgAbABwAHQAeAB8AIAAhACIAIwAkACUAJgAnACgAKQAqACsALAAtAC4ALwAwADEAMgQNcmV3YXJkQWRkcktleQkBFW1pbmVyUmV3YXJkQWRkcmVzc0tleQEFDGxlYXZpbmdNaW5lcgQRcHJldlJld2FyZEFkZHJlc3MJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkAnQgCBQR0aGlzBQ1yZXdhcmRBZGRyS2V5AhZtaW5lciBoYXMgbmV2ZXIgam9pbmVkAwkAAAIFEXByZXZSZXdhcmRBZGRyZXNzBRFwcmV2UmV3YXJkQWRkcmVzcwMJAAACBQ50aGlzRXBvY2hNaW5lcggFAWkMb3JpZ2luQ2FsbGVyCQACAQIcZGVzaWduYXRlZCBtaW5lciBjYW4ndCBsZWF2ZQkAzAgCCQELU3RyaW5nRW50cnkCBQxhbGxNaW5lcnNLZXkJALoJAgUPcmVtYWluaW5nTWluZXJzBQNTRVAFA25pbAkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgFpAQh0cmFuc2ZlcgEQZGVzdEVsQWRkcmVzc0hleAQMY2hlY2tBZGRyZXNzAwkAAAIJALECAQUQZGVzdEVsQWRkcmVzc0hleAUURVRIX0FERFJFU1NfU1RSX1NJWkUJAN0EAQUQZGVzdEVsQWRkcmVzc0hleAkAAgEJAKwCAgkArAICCQCsAgICFEFkZHJlc3Mgc2hvdWxkIGhhdmUgCQCkAwEFFEVUSF9BRERSRVNTX1NUUl9TSVpFAhEgY2hhcmFjdGVycywgZ290IAkApAMBCQCxAgEFEGRlc3RFbEFkZHJlc3NIZXgDCQAAAgUMY2hlY2tBZGRyZXNzBQxjaGVja0FkZHJlc3MEDGNoZWNrUGF5bWVudAMJAAACCQCQAwEIBQFpCHBheW1lbnRzAAEGCQACAQkArAICAi9FeHBlY3RlZCBvbmUgcGF5bWVudCBhcyBhIHRyYW5zZmVyIGFtb3VudCwgZ290IAkApAMBCQCQAwEIBQFpCHBheW1lbnRzAwkAAAIFDGNoZWNrUGF5bWVudAUMY2hlY2tQYXltZW50BAp0b2tlbklkU3RyCQERQGV4dHJOYXRpdmUoMTA1OCkBBQp0b2tlbklkS2V5BAd0b2tlbklkCQDZBAEFCnRva2VuSWRTdHIEAXQJAJEDAggFAWkIcGF5bWVudHMAAAQMY2hlY2tBc3NldElkBAckbWF0Y2gwCAUBdAdhc3NldElkAwkAAQIFByRtYXRjaDACCkJ5dGVWZWN0b3IEB2Fzc2V0SWQFByRtYXRjaDADCQAAAgUHYXNzZXRJZAUHdG9rZW5JZAYJAAIBCQCsAgIJAKwCAgkArAICAglFeHBlY3RlZCAFCnRva2VuSWRTdHICFSBpbiB0aGUgcGF5bWVudCwgZ290IAkA2AQBBQdhc3NldElkCQACAQkArAICCQCsAgICCUV4cGVjdGVkIAUKdG9rZW5JZFN0cgIaIGluIHRoZSBwYXltZW50LCBnb3QgV2F2ZXMDCQAAAgUMY2hlY2tBc3NldElkBQxjaGVja0Fzc2V0SWQECWxhc3RJbmRleAgJAQlibG9ja01ldGEBBRJtYWluQ2hhaW5MYXN0QmxvY2sCXzYECXF1ZXVlU2l6ZQkAZQIFFG5hdGl2ZVRyYW5zZmVyc0NvdW50BQlsYXN0SW5kZXgED2NoZWNrUXVldWVMaW1pdAMJAGYCAKABBQlxdWV1ZVNpemUJAQphbW91bnRHdEVxAwUBdADAhD0FCXF1ZXVlU2l6ZQMJAGYCAMAMBQlxdWV1ZVNpemUJAQphbW91bnRHdEVxAwUBdACAreIEBQlxdWV1ZVNpemUDCQBmAgCAGQUJcXVldWVTaXplCQEKYW1vdW50R3RFcQMFAXQAgMLXLwUJcXVldWVTaXplAwkAZgIAgDIFCXF1ZXVlU2l6ZQkBCmFtb3VudEd0RXEDBQF0AICU69wDBQlxdWV1ZVNpemUJAAIBCQCsAgIJAKwCAgIjVHJhbnNmZXJzIGRlbmllZCBmb3IgcXVldWUgc2l6ZSBvZiAJAKQDAQUJcXVldWVTaXplAiguIFdhaXQgdW50aWwgY3VycmVudCB0cmFuc2ZlcnMgcHJvY2Vzc2VkAwkAAAIFD2NoZWNrUXVldWVMaW1pdAUPY2hlY2tRdWV1ZUxpbWl0CQDMCAIJAQxJbnRlZ2VyRW50cnkCBRduYXRpdmVUcmFuc2ZlcnNDb3VudEtleQkAZAIFFG5hdGl2ZVRyYW5zZmVyc0NvdW50AAEJAMwIAgkBFW1rTmF0aXZlVHJhbnNmZXJFbnRyeQMFFG5hdGl2ZVRyYW5zZmVyc0NvdW50BRBkZXN0RWxBZGRyZXNzSGV4CAUBdAZhbW91bnQJAMwIAgkBBEJ1cm4CBQd0b2tlbklkCAUBdAZhbW91bnQFA25pbAkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgFpAQh3aXRoZHJhdwQMYmxvY2tIYXNoSGV4C21lcmtsZVByb29mFHRyYW5zZmVySW5kZXhJbkJsb2NrBmFtb3VudAQRd2l0aGRyYXdCbG9ja01ldGEJAQlibG9ja01ldGEBBQxibG9ja0hhc2hIZXgEE3dpdGhkcmF3QmxvY2tIZWlnaHQIBRF3aXRoZHJhd0Jsb2NrTWV0YQJfMQQUZmluYWxpemVkQmxvY2tIZWlnaHQICQEJYmxvY2tNZXRhAQkBEUBleHRyTmF0aXZlKDEwNTgpAQURZmluYWxpemVkQmxvY2tLZXkCXzEEGG1haW5DaGFpbkxhc3RCbG9ja0hlaWdodAgJAQlibG9ja01ldGEBBRJtYWluQ2hhaW5MYXN0QmxvY2sCXzEDCQBmAgUTd2l0aGRyYXdCbG9ja0hlaWdodAUUZmluYWxpemVkQmxvY2tIZWlnaHQJAAIBCQCsAgIJAKwCAgkArAICAgpFTCBibG9jayAjCQCkAwEFE3dpdGhkcmF3QmxvY2tIZWlnaHQCLSBpcyBub3QgZmluYWxpemVkLiBUaGUgY3VycmVudCBmaW5hbGl6ZWQgaXMgIwkApAMBBRRmaW5hbGl6ZWRCbG9ja0hlaWdodAMJAQIhPQIJAJADAQULbWVya2xlUHJvb2YFFFdJVEhEUkFXX1BST09GU19TSVpFCQACAQkArAICCQCsAgIJAKwCAgIJRXhwZWN0ZWQgCQCkAwEFFFdJVEhEUkFXX1BST09GU19TSVpFAg0gcHJvb2ZzLCBnb3QgCQCkAwEJAJADAQULbWVya2xlUHJvb2YDCQBmAgAABRR0cmFuc2ZlckluZGV4SW5CbG9jawkAAgEJAKwCAgIzVHJhbnNmZXIgaW5kZXggaW4gYmxvY2sgc2hvdWxkIGJlIG5vbm5lZ2F0aXZlLCBnb3QgCQCkAwEFFHRyYW5zZmVySW5kZXhJbkJsb2NrAwkAZwIAAAUGYW1vdW50CQACAQkArAICAh9BbW91bnQgc2hvdWxkIGJlIHBvc2l0aXZlLCBnb3QgCQCkAwEFBmFtb3VudAQUd2l0aGRyYXdCbG9ja0NoYWluSWQIBRF3aXRoZHJhd0Jsb2NrTWV0YQJfNAQLaXNNYWluQ2hhaW4JAAACBRR3aXRoZHJhd0Jsb2NrQ2hhaW5JZAULbWFpbkNoYWluSWQEEnJlbGF0ZXNUb01haW5DaGFpbgQHJG1hdGNoMAkAnwgBCQEUY2hhaW5Gb3JrZWRIZWlnaHRLZXkBBRR3aXRoZHJhd0Jsb2NrQ2hhaW5JZAMJAAECBQckbWF0Y2gwAgNJbnQEDGZvcmtlZEhlaWdodAUHJG1hdGNoMAkAZgIFDGZvcmtlZEhlaWdodAUTd2l0aGRyYXdCbG9ja0hlaWdodAkAAgEJAKwCAgkArAICCQCsAgIFDGJsb2NrSGFzaEhleAIdIGlzIG9uIGFuIGFsdGVybmF0aXZlIGNoYWluICMJAKQDAQUUd2l0aGRyYXdCbG9ja0NoYWluSWQCOCB0aGF0IHdhcyBub3QgYXBwcm92ZWQgYnkgbWFqb3JpdHkuIFdhaXQgZm9yIHNvbWUgYmxvY2tzAwMFC2lzTWFpbkNoYWluBgUScmVsYXRlc1RvTWFpbkNoYWluBAlyZWNpcGllbnQIBQFpDG9yaWdpbkNhbGxlcgQPcmVjaXBpZW50UGtIYXNoCQDJAQIJAMoBAggFCXJlY2lwaWVudAVieXRlcwACBRRQVUJMSUNfS0VZX0hBU0hfU0laRQQPemVyb0Ftb3VudEJ5dGVzASwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQLYW1vdW50Qnl0ZXMJAJoDAQUGYW1vdW50BAtlbEV2ZW50RGF0YQkAywECCQDLAQIFD3JlY2lwaWVudFBrSGFzaAkAyQECBQ96ZXJvQW1vdW50Qnl0ZXMJAGUCCQDIAQEFD3plcm9BbW91bnRCeXRlcwkAyAEBBQthbW91bnRCeXRlcwULYW1vdW50Qnl0ZXMEEWVsRXZlbnREYXRhRGlnZXN0CQDwFQEFC2VsRXZlbnREYXRhBBJjYWxjdWxhdGVkUm9vdEhhc2gJAL0FAwULbWVya2xlUHJvb2YFEWVsRXZlbnREYXRhRGlnZXN0BRR0cmFuc2ZlckluZGV4SW5CbG9jawQQZXhwZWN0ZWRSb290SGFzaAgFEXdpdGhkcmF3QmxvY2tNZXRhAl81AwkAAAIFEmNhbGN1bGF0ZWRSb290SGFzaAUQZXhwZWN0ZWRSb290SGFzaAQHdG9rZW5JZAkA2QQBCQERQGV4dHJOYXRpdmUoMTA1OCkBBQp0b2tlbklkS2V5BAx0cmFuc2ZlcnNLZXkJARRibG9ja0UyQ1RyYW5zZmVyc0tleQEFDGJsb2NrSGFzaEhleAkAzAgCCQEHUmVpc3N1ZQMFB3Rva2VuSWQFBmFtb3VudAYJAMwIAgkBDlNjcmlwdFRyYW5zZmVyAwUJcmVjaXBpZW50BQZhbW91bnQFB3Rva2VuSWQJAMwIAgkBC1N0cmluZ0VudHJ5AgUMdHJhbnNmZXJzS2V5CQEJc2V0T3JGYWlsAgkBC3ZhbHVlT3JFbHNlAgkAoggBBQx0cmFuc2ZlcnNLZXkCAAUUdHJhbnNmZXJJbmRleEluQmxvY2sFA25pbAkAAgEJAKwCAgkArAICCQCsAgIJAKwCAgkArAICCQCsAgICFEV4cGVjdGVkIHJvb3QgaGFzaDogCQDcBAEFEGV4cGVjdGVkUm9vdEhhc2gCBywgZ290OiAJANwEAQUSY2FsY3VsYXRlZFJvb3RIYXNoAhUuIEV2ZW50IGRhdGEgZGlnZXN0OiAJANoEAQURZWxFdmVudERhdGFEaWdlc3QCHy4gQ2hlY2sgeW91ciB3aXRoZHJhdyBhcmd1bWVudHMJAAIBCQCsAgIJAKwCAgIJRXhwZWN0ZWQgBQxibG9ja0hhc2hIZXgCLyB0byBiZSBlaXRoZXIgb24gdGhlIG1haW4gY2hhaW4gb3IgcmVsYXRlIHRvIGl0AWkBBXNldHVwAhNnZW5lc2lzQmxvY2tIYXNoSGV4EW1pbmVyUmV3YXJkSW5Hd2VpAwkBD2lzQ29udHJhY3RTZXR1cAAJAAIBAiRUaGUgY29udHJhY3QgaGFzIGJlZW4gYWxyZWFkeSBzZXQgdXADCQBmAgAABRFtaW5lclJld2FyZEluR3dlaQkAAgECJFRoZSBtaW5lciByZXdhcmQgbXVzdCBiZSBub25uZWdhdGl2ZQQQZ2VuZXNpc0Jsb2NrSGFzaAkA3QQBBRNnZW5lc2lzQmxvY2tIYXNoSGV4BBljaGVja0dlbmVzaXNCbG9ja0hhc2hTaXplAwkAAAIJAMgBAQUQZ2VuZXNpc0Jsb2NrSGFzaAUPQkxPQ0tfSEFTSF9TSVpFBgkAAgECGFdyb25nIGdlbmVzaXMgYmxvY2sgaGFzaAMJAAACBRljaGVja0dlbmVzaXNCbG9ja0hhc2hTaXplBRljaGVja0dlbmVzaXNCbG9ja0hhc2hTaXplBAdlbXB0eVBrASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQTZ2VuZXNpc01pbmVyQWRkcmVzcwkApwgBBQdlbXB0eVBrBBdnZW5lc2lzRXRoUmV3YXJkQWRkcmVzcwEUAAAAAAAAAAAAAAAAAAAAAAAAAAAEGWdlbmVzaXNCbG9ja1JlZmVyZW5jZUhhc2gCQDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAEBWlzc3VlCQDCCAUCBVVOSVQwAgxOYXRpdmUgdG9rZW4AAAAIBgQHdG9rZW5JZAkAuAgBBQVpc3N1ZQQQZ2VuZXNpc0Jsb2NrTWV0YQkBEG1rQmxvY2tNZXRhRW50cnkGBRNnZW5lc2lzQmxvY2tIYXNoSGV4AAAFGWdlbmVzaXNCbG9ja1JlZmVyZW5jZUhhc2gAAAkA3AQBAQAA////////////AQkAzAgCBRBnZW5lc2lzQmxvY2tNZXRhCQDMCAIJAQtTdHJpbmdFbnRyeQIJARRjaGFpbkZpcnN0QmxvY2tJZEtleQEAAAUTZ2VuZXNpc0Jsb2NrSGFzaEhleAkAzAgCCQEQbWtDaGFpbk1ldGFFbnRyeQMAAAAABRNnZW5lc2lzQmxvY2tIYXNoSGV4CQDMCAIJAQxJbnRlZ2VyRW50cnkCBQ5taW5lclJld2FyZEtleQURbWluZXJSZXdhcmRJbkd3ZWkJAMwIAgkBC1N0cmluZ0VudHJ5AgkBDGVwb2NoTWV0YUtleQEFBmhlaWdodAkArAICCQCsAgIJAKUIAQUTZ2VuZXNpc01pbmVyQWRkcmVzcwIDLDAsBRNnZW5lc2lzQmxvY2tIYXNoSGV4CQDMCAIJAQtTdHJpbmdFbnRyeQIFEWZpbmFsaXplZEJsb2NrS2V5BRNnZW5lc2lzQmxvY2tIYXNoSGV4CQDMCAIFBWlzc3VlCQDMCAIJAQtTdHJpbmdFbnRyeQIFCnRva2VuSWRLZXkJANgEAQUHdG9rZW5JZAkAzAgCCQELU3RyaW5nRW50cnkCBRJlbEJyaWRnZUFkZHJlc3NLZXkCKjB4MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNmE3ZQUDbmlsCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAC8QzI4=", "height": 3288973, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: none Next: 36fQejUN2YzRW7ooET6swSK1pmxdjwxwfafH8YBAXWyc Full:
Old | New | Differences | |
---|---|---|---|
1 | - | # no script | |
1 | + | {-# STDLIB_VERSION 8 #-} | |
2 | + | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | + | {-# CONTENT_TYPE DAPP #-} | |
4 | + | let INT_MAX = 9223372036854775807 | |
5 | + | ||
6 | + | let WAVES = 100000000 | |
7 | + | ||
8 | + | let MIN_BALANCE = (20000 * WAVES) | |
9 | + | ||
10 | + | let SEP = "," | |
11 | + | ||
12 | + | let BLOCK_HASH_SIZE = 32 | |
13 | + | ||
14 | + | let PUBLIC_KEY_HASH_SIZE = 20 | |
15 | + | ||
16 | + | let ROOT_HASH_SIZE = 32 | |
17 | + | ||
18 | + | let WITHDRAW_PROOFS_SIZE = 10 | |
19 | + | ||
20 | + | let ETH_ADDRESS_STR_SIZE = 40 | |
21 | + | ||
22 | + | let MAX_CL_TO_EL_TRANSFERS = 16 | |
23 | + | ||
24 | + | let zeroesStr = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" | |
25 | + | ||
26 | + | let allMinersKey = "allMiners" | |
27 | + | ||
28 | + | let mainChainIdKey = "mainChainId" | |
29 | + | ||
30 | + | let lastChainIdKey = "lastChainId" | |
31 | + | ||
32 | + | let firstValidAltChainIdKey = "firstValidAltChainId" | |
33 | + | ||
34 | + | let minerRewardKey = "minerReward" | |
35 | + | ||
36 | + | let blockMetaK = "block_0x" | |
37 | + | ||
38 | + | let finalizedBlockKey = "finalizedBlock" | |
39 | + | ||
40 | + | let tokenIdKey = "tokenId" | |
41 | + | ||
42 | + | let elBridgeAddressKey = "elBridgeAddress" | |
43 | + | ||
44 | + | let nativeTransfersCountKey = "nativeTransfersCount" | |
45 | + | ||
46 | + | func pad (i) = { | |
47 | + | let s = toString(i) | |
48 | + | match size(s) { | |
49 | + | case _ => | |
50 | + | if ((1 == $match0)) | |
51 | + | then ("0000000" + s) | |
52 | + | else if ((2 == $match0)) | |
53 | + | then ("000000" + s) | |
54 | + | else if ((3 == $match0)) | |
55 | + | then ("00000" + s) | |
56 | + | else if ((4 == $match0)) | |
57 | + | then ("0000" + s) | |
58 | + | else if ((5 == $match0)) | |
59 | + | then ("000" + s) | |
60 | + | else if ((6 == $match0)) | |
61 | + | then ("00" + s) | |
62 | + | else if ((7 == $match0)) | |
63 | + | then ("0" + s) | |
64 | + | else s | |
65 | + | } | |
66 | + | } | |
67 | + | ||
68 | + | ||
69 | + | func blockE2CTransfersKey (blockHashHex) = ("elToClTransfers_0x" + blockHashHex) | |
70 | + | ||
71 | + | ||
72 | + | func epochMetaKey (epoch) = ("epoch_" + pad(epoch)) | |
73 | + | ||
74 | + | ||
75 | + | func chainFirstBlockIdKey (chainId) = (("chain" + toString(chainId)) + "FirstBlock") | |
76 | + | ||
77 | + | ||
78 | + | func chainMetaKey (chainId) = ("chain_" + pad(chainId)) | |
79 | + | ||
80 | + | ||
81 | + | func chainLastHeightKey (chainId,miner) = ((("chain_" + pad(chainId)) + "_") + toString(miner)) | |
82 | + | ||
83 | + | ||
84 | + | func chainForkedHeightKey (chainId) = (("chain_" + pad(chainId)) + "ForkedHeight") | |
85 | + | ||
86 | + | ||
87 | + | func supportersKey (chainId) = (("chain" + toString(chainId)) + "Supporters") | |
88 | + | ||
89 | + | ||
90 | + | func minerRewardAddressKey (minerAddr) = (("miner_" + minerAddr) + "_RewardAddress") | |
91 | + | ||
92 | + | ||
93 | + | func minerPkKey (rewardAddress) = (("miner_0x" + rewardAddress) + "_PK") | |
94 | + | ||
95 | + | ||
96 | + | func minerChainIdKey (miner) = (("miner_" + toString(miner)) + "_ChainId") | |
97 | + | ||
98 | + | ||
99 | + | let nativeTransfersCount = valueOrElse(getInteger(this, nativeTransfersCountKey), 0) | |
100 | + | ||
101 | + | func nativeTransferKey (index) = ("nativeTransfer_" + toString(index)) | |
102 | + | ||
103 | + | ||
104 | + | func mkNativeTransferEntry (index,destElAddressHex,amount) = StringEntry(nativeTransferKey(index), ((("0x" + destElAddressHex) + SEP) + toString(amount))) | |
105 | + | ||
106 | + | ||
107 | + | func ensureCorrectTransfers (refTransferIndex,transferIndex,expectReward) = { | |
108 | + | let maxTransfers = if (expectReward) | |
109 | + | then (MAX_CL_TO_EL_TRANSFERS - 1) | |
110 | + | else MAX_CL_TO_EL_TRANSFERS | |
111 | + | let actualTransfers = (transferIndex - refTransferIndex) | |
112 | + | let checkNumber = if ((actualTransfers > maxTransfers)) | |
113 | + | then throw(((("Allowed only " + toString(maxTransfers)) + " transfers, got ") + toString(actualTransfers))) | |
114 | + | else true | |
115 | + | if ((checkNumber == checkNumber)) | |
116 | + | then if ((transferIndex >= nativeTransfersCount)) | |
117 | + | then throw(((("Attempt to transfer #" + toString(transferIndex)) + ". Available transfers: ") + toString(nativeTransfersCount))) | |
118 | + | else true | |
119 | + | else throw("Strict value is not equal to itself.") | |
120 | + | } | |
121 | + | ||
122 | + | ||
123 | + | func amountGtEq (t,gtEq,queueSize) = if ((t.amount >= gtEq)) | |
124 | + | then true | |
125 | + | else throw((((((("Transferring amount " + toString(t.amount)) + " should be >= ") + toString(gtEq)) + " for queue size of ") + toString(queueSize)) + ". Transfer more or wait")) | |
126 | + | ||
127 | + | ||
128 | + | func generatingBalance (address) = wavesBalance(address).generating | |
129 | + | ||
130 | + | ||
131 | + | func chainMeta (chainId) = { | |
132 | + | let s = getStringValue(chainMetaKey(chainId)) | |
133 | + | let items = split(s, SEP) | |
134 | + | $Tuple2(parseIntValue(items[0]), items[1]) | |
135 | + | } | |
136 | + | ||
137 | + | ||
138 | + | func mkChainMetaEntry (chainId,newChainHeight,blockHashHex) = StringEntry(chainMetaKey(chainId), ((toString(newChainHeight) + SEP) + blockHashHex)) | |
139 | + | ||
140 | + | ||
141 | + | let mainChainId = valueOrElse(getInteger(mainChainIdKey), 0) | |
142 | + | ||
143 | + | let $t050295095 = chainMeta(mainChainId) | |
144 | + | ||
145 | + | let mainChainHeight = $t050295095._1 | |
146 | + | ||
147 | + | let mainChainLastBlock = $t050295095._2 | |
148 | + | ||
149 | + | func epochMeta (epoch) = match getString(epochMetaKey(epoch)) { | |
150 | + | case s: String => | |
151 | + | let fragments = split(s, SEP) | |
152 | + | $Tuple3(addressFromStringValue(fragments[0]), parseIntValue(fragments[1]), fragments[2]) | |
153 | + | case _ => | |
154 | + | unit | |
155 | + | } | |
156 | + | ||
157 | + | ||
158 | + | let $t053265450 = match epochMeta(height) { | |
159 | + | case m: (Address, Int, String) => | |
160 | + | m | |
161 | + | case _ => | |
162 | + | $Tuple2(unit, 0) | |
163 | + | } | |
164 | + | ||
165 | + | let thisEpochMiner = $t053265450._1 | |
166 | + | ||
167 | + | let thisEpochRef = $t053265450._2 | |
168 | + | ||
169 | + | let allMinersStr = valueOrElse(getString(allMinersKey), "") | |
170 | + | ||
171 | + | let allMiners = match allMinersStr { | |
172 | + | case _ => | |
173 | + | if (("" == $match0)) | |
174 | + | then nil | |
175 | + | else if ($isInstanceOf($match0, "String")) | |
176 | + | then { | |
177 | + | let raw = $match0 | |
178 | + | split_4C(raw, SEP) | |
179 | + | } | |
180 | + | else throw("Match error") | |
181 | + | } | |
182 | + | ||
183 | + | func blockMeta (blockId) = { | |
184 | + | let meta = getBinaryValue((blockMetaK + blockId)) | |
185 | + | let metaSize = size(meta) | |
186 | + | let blockHeight = toInt(meta) | |
187 | + | let blockEpoch = toInt(meta, 8) | |
188 | + | let blockParent = take(drop(meta, 16), BLOCK_HASH_SIZE) | |
189 | + | let chainId = toInt(meta, (16 + BLOCK_HASH_SIZE)) | |
190 | + | let baseOffset = (24 + BLOCK_HASH_SIZE) | |
191 | + | let remainingBytes = (metaSize - baseOffset) | |
192 | + | let e2cTransfersRootHash = if ((remainingBytes >= ROOT_HASH_SIZE)) | |
193 | + | then take(drop(meta, baseOffset), ROOT_HASH_SIZE) | |
194 | + | else base58'' | |
195 | + | let lastC2ETransferIndex = if (if ((remainingBytes == 8)) | |
196 | + | then true | |
197 | + | else (remainingBytes > ROOT_HASH_SIZE)) | |
198 | + | then toInt(meta, (baseOffset + size(e2cTransfersRootHash))) | |
199 | + | else -1 | |
200 | + | $Tuple6(blockHeight, blockEpoch, blockParent, chainId, e2cTransfersRootHash, lastC2ETransferIndex) | |
201 | + | } | |
202 | + | ||
203 | + | ||
204 | + | func mkBlockMetaEntry (blockHashHex,blockHeight,blockParentHex,chainId,e2cTransfersRootHashHex,lastC2ETransferIndex) = { | |
205 | + | let e2cTransfersRootHashBytes = fromBase16String(e2cTransfersRootHashHex) | |
206 | + | let rootHashBytesSize = size(e2cTransfersRootHashBytes) | |
207 | + | let checkRootHash = if (if ((rootHashBytesSize == 0)) | |
208 | + | then true | |
209 | + | else (rootHashBytesSize == ROOT_HASH_SIZE)) | |
210 | + | then true | |
211 | + | else throw(((("Transfers root hash should have 0 or " + toString(ROOT_HASH_SIZE)) + " bytes, got ") + toString(rootHashBytesSize))) | |
212 | + | if ((checkRootHash == checkRootHash)) | |
213 | + | then { | |
214 | + | let blockMetaBytes = (((((toBytes(blockHeight) + toBytes(height)) + fromBase16String(blockParentHex)) + toBytes(chainId)) + e2cTransfersRootHashBytes) + toBytes(lastC2ETransferIndex)) | |
215 | + | BinaryEntry((blockMetaK + blockHashHex), blockMetaBytes) | |
216 | + | } | |
217 | + | else throw("Strict value is not equal to itself.") | |
218 | + | } | |
219 | + | ||
220 | + | ||
221 | + | func lastHeightBy (miner,chainId) = match getInteger(chainLastHeightKey(chainId, miner)) { | |
222 | + | case h: Int => | |
223 | + | h | |
224 | + | case _ => | |
225 | + | let blockHash = getStringValue(((("chain" + toString(chainId)) + "LastMinedBy") + toString(miner))) | |
226 | + | blockMeta(blockHash)._1 | |
227 | + | } | |
228 | + | ||
229 | + | ||
230 | + | let $t074908425 = { | |
231 | + | let hitSource = match lastBlock.vrf { | |
232 | + | case vrf: ByteVector => | |
233 | + | vrf | |
234 | + | case _ => | |
235 | + | lastBlock.generationSignature | |
236 | + | } | |
237 | + | func processMiner (prev,miner) = { | |
238 | + | let $t077577820 = prev | |
239 | + | let prevMiner = $t077577820._1 | |
240 | + | let prevTotalBalance = $t077577820._2 | |
241 | + | let prevDelay = $t077577820._3 | |
242 | + | let prevMiners = $t077577820._4 | |
243 | + | let minerAddress = addressFromStringValue(miner) | |
244 | + | let wavesGenBalance = wavesBalance(minerAddress).generating | |
245 | + | let minerBalance = generatingBalance(minerAddress) | |
246 | + | if (if ((MIN_BALANCE > wavesGenBalance)) | |
247 | + | then true | |
248 | + | else (0 >= minerBalance)) | |
249 | + | then prev | |
250 | + | else { | |
251 | + | let nextDelay = calculateDelay(minerAddress, minerBalance) | |
252 | + | if ((prevDelay > nextDelay)) | |
253 | + | then $Tuple4(miner, (prevTotalBalance + minerBalance), nextDelay, (prevMiners :+ miner)) | |
254 | + | else $Tuple4(prevMiner, (prevTotalBalance + minerBalance), prevDelay, (prevMiners :+ miner)) | |
255 | + | } | |
256 | + | } | |
257 | + | ||
258 | + | let $l = allMiners | |
259 | + | let $s = size($l) | |
260 | + | let $acc0 = $Tuple4("", 0, INT_MAX, nil) | |
261 | + | func $f0_1 ($a,$i) = if (($i >= $s)) | |
262 | + | then $a | |
263 | + | else processMiner($a, $l[$i]) | |
264 | + | ||
265 | + | func $f0_2 ($a,$i) = if (($i >= $s)) | |
266 | + | then $a | |
267 | + | else throw("List size exceeds 50") | |
268 | + | ||
269 | + | $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10), 11), 12), 13), 14), 15), 16), 17), 18), 19), 20), 21), 22), 23), 24), 25), 26), 27), 28), 29), 30), 31), 32), 33), 34), 35), 36), 37), 38), 39), 40), 41), 42), 43), 44), 45), 46), 47), 48), 49), 50) | |
270 | + | } | |
271 | + | ||
272 | + | let computedGenerator = $t074908425._1 | |
273 | + | ||
274 | + | let computedTotalBalance = $t074908425._2 | |
275 | + | ||
276 | + | let $t084278493 = blockMeta(mainChainLastBlock) | |
277 | + | ||
278 | + | let mclbIgnored1 = $t084278493._1 | |
279 | + | ||
280 | + | let mainChainEpoch = $t084278493._2 | |
281 | + | ||
282 | + | func calculateFinalizedBlockHash (curMiner,curPrevEpoch,curLastBlockHash) = { | |
283 | + | let offsets_100 = split_4C("::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::", "") | |
284 | + | let halfBalance = (computedTotalBalance / 2) | |
285 | + | func step (prev,next) = { | |
286 | + | let $t088598923 = prev | |
287 | + | let thisEpoch = $t088598923._1 | |
288 | + | let maybeSafeEpoch = $t088598923._2 | |
289 | + | let totalBalance = $t088598923._3 | |
290 | + | let prevMiners = $t088598923._4 | |
291 | + | match maybeSafeEpoch { | |
292 | + | case _: Unit => | |
293 | + | let $t089819147 = if ((thisEpoch == height)) | |
294 | + | then $Tuple3(curMiner, curPrevEpoch, curLastBlockHash) | |
295 | + | else value(epochMeta(thisEpoch)) | |
296 | + | let miner = $t089819147._1 | |
297 | + | let prevEpoch = $t089819147._2 | |
298 | + | let lastBlockHash = $t089819147._3 | |
299 | + | if (if ((prevEpoch == 0)) | |
300 | + | then true | |
301 | + | else ((height - thisEpoch) >= 100)) | |
302 | + | then $Tuple4(thisEpoch, lastBlockHash, totalBalance, allMiners) | |
303 | + | else { | |
304 | + | let $t093019503 = if (containsElement(prevMiners, miner)) | |
305 | + | then $Tuple2(totalBalance, prevMiners) | |
306 | + | else $Tuple2((totalBalance + generatingBalance(miner)), miner :: prevMiners) | |
307 | + | let newTotalBalance = $t093019503._1 | |
308 | + | let newMiners = $t093019503._2 | |
309 | + | if ((newTotalBalance > halfBalance)) | |
310 | + | then $Tuple4(thisEpoch, lastBlockHash, newTotalBalance, allMiners) | |
311 | + | else $Tuple4(prevEpoch, unit, newTotalBalance, newMiners) | |
312 | + | } | |
313 | + | case _ => | |
314 | + | prev | |
315 | + | } | |
316 | + | } | |
317 | + | ||
318 | + | let $t097369832 = { | |
319 | + | let $l = offsets_100 | |
320 | + | let $s = size($l) | |
321 | + | let $acc0 = $Tuple4(height, unit, 0, nil) | |
322 | + | func $f0_1 ($a,$i) = if (($i >= $s)) | |
323 | + | then $a | |
324 | + | else step($a, $l[$i]) | |
325 | + | ||
326 | + | func $f0_2 ($a,$i) = if (($i >= $s)) | |
327 | + | then $a | |
328 | + | else throw("List size exceeds 100") | |
329 | + | ||
330 | + | $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10), 11), 12), 13), 14), 15), 16), 17), 18), 19), 20), 21), 22), 23), 24), 25), 26), 27), 28), 29), 30), 31), 32), 33), 34), 35), 36), 37), 38), 39), 40), 41), 42), 43), 44), 45), 46), 47), 48), 49), 50), 51), 52), 53), 54), 55), 56), 57), 58), 59), 60), 61), 62), 63), 64), 65), 66), 67), 68), 69), 70), 71), 72), 73), 74), 75), 76), 77), 78), 79), 80), 81), 82), 83), 84), 85), 86), 87), 88), 89), 90), 91), 92), 93), 94), 95), 96), 97), 98), 99), 100) | |
331 | + | } | |
332 | + | let fallbackEpoch = $t097369832._1 | |
333 | + | let finalizedBlockHashOpt = $t097369832._2 | |
334 | + | match finalizedBlockHashOpt { | |
335 | + | case finalizedBlockHash: String => | |
336 | + | finalizedBlockHash | |
337 | + | case _ => | |
338 | + | value(epochMeta(fallbackEpoch))._3 | |
339 | + | } | |
340 | + | } | |
341 | + | ||
342 | + | ||
343 | + | func supportingBalance (chainId) = { | |
344 | + | func addBalance (acc,generatorStr) = { | |
345 | + | let $t01010210138 = acc | |
346 | + | let totalBalance = $t01010210138._1 | |
347 | + | let generators = $t01010210138._2 | |
348 | + | let generator = addressFromStringValue(generatorStr) | |
349 | + | if (containsElement(generators, generator)) | |
350 | + | then acc | |
351 | + | else { | |
352 | + | let balance = generatingBalance(generator) | |
353 | + | $Tuple2((totalBalance + balance), (generators :+ generator)) | |
354 | + | } | |
355 | + | } | |
356 | + | ||
357 | + | let allGenerators = split_4C(getStringValue(supportersKey(chainId)), SEP) | |
358 | + | let $t01046010525 = { | |
359 | + | let $l = allGenerators | |
360 | + | let $s = size($l) | |
361 | + | let $acc0 = $Tuple2(0, nil) | |
362 | + | func $f0_1 ($a,$i) = if (($i >= $s)) | |
363 | + | then $a | |
364 | + | else addBalance($a, $l[$i]) | |
365 | + | ||
366 | + | func $f0_2 ($a,$i) = if (($i >= $s)) | |
367 | + | then $a | |
368 | + | else throw("List size exceeds 100") | |
369 | + | ||
370 | + | $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10), 11), 12), 13), 14), 15), 16), 17), 18), 19), 20), 21), 22), 23), 24), 25), 26), 27), 28), 29), 30), 31), 32), 33), 34), 35), 36), 37), 38), 39), 40), 41), 42), 43), 44), 45), 46), 47), 48), 49), 50), 51), 52), 53), 54), 55), 56), 57), 58), 59), 60), 61), 62), 63), 64), 65), 66), 67), 68), 69), 70), 71), 72), 73), 74), 75), 76), 77), 78), 79), 80), 81), 82), 83), 84), 85), 86), 87), 88), 89), 90), 91), 92), 93), 94), 95), 96), 97), 98), 99), 100) | |
371 | + | } | |
372 | + | let balance = $t01046010525._1 | |
373 | + | let _g = $t01046010525._2 | |
374 | + | balance | |
375 | + | } | |
376 | + | ||
377 | + | ||
378 | + | func isContractSetup () = isDefined(getInteger(minerRewardKey)) | |
379 | + | ||
380 | + | ||
381 | + | func ensureMiningEpoch (generator) = if ((toString(generator) != computedGenerator)) | |
382 | + | then throw(((((toBase58String(generator.bytes) + " is not allowed to mine in ") + toString(height)) + " epoch. Expected ") + computedGenerator)) | |
383 | + | else unit | |
384 | + | ||
385 | + | ||
386 | + | func isReferenceCorrect (reference,lastBlock) = if ((reference == lastBlock)) | |
387 | + | then unit | |
388 | + | else throw(((("Expected a reference to the chain last block: 0x" + lastBlock) + ". Got: 0x") + reference)) | |
389 | + | ||
390 | + | ||
391 | + | func chainIsInactive (chainId) = { | |
392 | + | let firstBlockId = getStringValue(chainFirstBlockIdKey(chainId)) | |
393 | + | let firstValidAltChainId = valueOrElse(getInteger(firstValidAltChainIdKey), 0) | |
394 | + | if ((firstValidAltChainId > chainId)) | |
395 | + | then true | |
396 | + | else (blockMeta(getStringValue(finalizedBlockKey))._1 > blockMeta(firstBlockId)._1) | |
397 | + | } | |
398 | + | ||
399 | + | ||
400 | + | func minerChainId (miner) = valueOrElse(getInteger(minerChainIdKey(miner)), getInteger(("chainIdOf" + toString(miner)))) | |
401 | + | ||
402 | + | ||
403 | + | func ensureExpectedOrInactiveChain (generator,expectedChainId,checkHeightBlock) = { | |
404 | + | let heightIsCorrect = match checkHeightBlock { | |
405 | + | case blockHash: String => | |
406 | + | let lastMinedBlockHeight = lastHeightBy(generator, mainChainId) | |
407 | + | ((blockMeta(blockHash)._1 + 1) > lastMinedBlockHeight) | |
408 | + | case _ => | |
409 | + | true | |
410 | + | } | |
411 | + | match minerChainId(generator) { | |
412 | + | case currentId: Int => | |
413 | + | if (if ((currentId == expectedChainId)) | |
414 | + | then true | |
415 | + | else if (chainIsInactive(currentId)) | |
416 | + | then heightIsCorrect | |
417 | + | else false) | |
418 | + | then unit | |
419 | + | else throw(("miner is mining other chain " + toString(currentId))) | |
420 | + | case _ => | |
421 | + | unit | |
422 | + | } | |
423 | + | } | |
424 | + | ||
425 | + | ||
426 | + | let heightString = (" at height " + toString(height)) | |
427 | + | ||
428 | + | func vrfAt (height) = valueOrErrorMessage(valueOrErrorMessage(blockInfoByHeight(height), ("last block is not available" + heightString)).vrf, ("VRF is not available" + heightString)) | |
429 | + | ||
430 | + | ||
431 | + | func ensureCorrectEpoch (expectedVRF) = { | |
432 | + | let actualVRF = vrfAt(height) | |
433 | + | if ((expectedVRF == actualVRF)) | |
434 | + | then unit | |
435 | + | else throw((((("Expected VRF " + toBase58String(expectedVRF)) + " does not match actual ") + toBase58String(actualVRF)) + heightString)) | |
436 | + | } | |
437 | + | ||
438 | + | ||
439 | + | func addSupporter (chainId,generator) = { | |
440 | + | let supportersStr = getStringValue(supportersKey(chainId)) | |
441 | + | let supporters = split_4C(supportersStr, SEP) | |
442 | + | if (containsElement(supporters, toString(generator))) | |
443 | + | then nil | |
444 | + | else [StringEntry(supportersKey(chainId), ((supportersStr + SEP) + toString(generator)))] | |
445 | + | } | |
446 | + | ||
447 | + | ||
448 | + | func setOrFail (flags,index) = if ((0 > index)) | |
449 | + | then throw(("Can't withdraw at negative index: " + toString(index))) | |
450 | + | else { | |
451 | + | let flagsSize = size(flags) | |
452 | + | if ((index >= flagsSize)) | |
453 | + | then { | |
454 | + | let addZeroes = (index - flagsSize) | |
455 | + | if ((addZeroes > size(zeroesStr))) | |
456 | + | then throw((("Can't add " + toString(addZeroes)) + " empty flags. Contact with developers")) | |
457 | + | else ((flags + take(zeroesStr, addZeroes)) + "1") | |
458 | + | } | |
459 | + | else { | |
460 | + | let tail = drop(flags, index) | |
461 | + | let atIndex = take(tail, 1) | |
462 | + | if ((atIndex == "0")) | |
463 | + | then ((take(flags, index) + "1") + drop(tail, 1)) | |
464 | + | else throw((("Transfer #" + toString(index)) + " has been already taken")) | |
465 | + | } | |
466 | + | } | |
467 | + | ||
468 | + | ||
469 | + | func validateBlockHash (hexStr) = { | |
470 | + | let decodedBytes = fromBase16String(hexStr) | |
471 | + | if ((size(decodedBytes) != BLOCK_HASH_SIZE)) | |
472 | + | then throw("invalid block id length") | |
473 | + | else hexStr | |
474 | + | } | |
475 | + | ||
476 | + | ||
477 | + | func getUpdateFinalizedBlockAction (caller,newBlockHashHex,prevEpoch) = { | |
478 | + | let curFinalizedBlockHeight = blockMeta(getStringValue(finalizedBlockKey))._1 | |
479 | + | let newFinalizedBlockHash = calculateFinalizedBlockHash(caller, prevEpoch, newBlockHashHex) | |
480 | + | if (if ((newFinalizedBlockHash == newBlockHashHex)) | |
481 | + | then true | |
482 | + | else (blockMeta(newFinalizedBlockHash)._1 > curFinalizedBlockHeight)) | |
483 | + | then [StringEntry(finalizedBlockKey, newFinalizedBlockHash)] | |
484 | + | else nil | |
485 | + | } | |
486 | + | ||
487 | + | ||
488 | + | @Callable(i) | |
489 | + | func extendMainChain (blockHashHex,referenceHex,vrf,e2cTransfersRootHashHex,lastC2ETransferIndex) = { | |
490 | + | let checkBlockHash = validateBlockHash(blockHashHex) | |
491 | + | if ((checkBlockHash == checkBlockHash)) | |
492 | + | then { | |
493 | + | let checkEpoch = ensureCorrectEpoch(vrf) | |
494 | + | if ((checkEpoch == checkEpoch)) | |
495 | + | then { | |
496 | + | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, mainChainId, unit) | |
497 | + | if ((checkChain == checkChain)) | |
498 | + | then { | |
499 | + | let checkReference = isReferenceCorrect(referenceHex, mainChainLastBlock) | |
500 | + | if ((checkReference == checkReference)) | |
501 | + | then { | |
502 | + | let checkTransfers = ensureCorrectTransfers(blockMeta(referenceHex)._6, lastC2ETransferIndex, true) | |
503 | + | if ((checkTransfers == checkTransfers)) | |
504 | + | then { | |
505 | + | let thisEpochMeta = match epochMeta(height) { | |
506 | + | case _: Unit => | |
507 | + | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(mainChainEpoch)) + SEP) + blockHashHex)) | |
508 | + | case other => | |
509 | + | throw("Epoch already started") | |
510 | + | } | |
511 | + | if ((thisEpochMeta == thisEpochMeta)) | |
512 | + | then { | |
513 | + | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
514 | + | if ((checkGenerator == checkGenerator)) | |
515 | + | then { | |
516 | + | let updateFinalizedBlock = getUpdateFinalizedBlockAction(i.originCaller, blockHashHex, mainChainEpoch) | |
517 | + | let newChainHeight = (mainChainHeight + 1) | |
518 | + | ([mkBlockMetaEntry(blockHashHex, newChainHeight, mainChainLastBlock, mainChainId, e2cTransfersRootHashHex, lastC2ETransferIndex), mkChainMetaEntry(mainChainId, newChainHeight, blockHashHex), IntegerEntry(minerChainIdKey(i.originCaller), mainChainId), IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), newChainHeight), thisEpochMeta] ++ updateFinalizedBlock) | |
519 | + | } | |
520 | + | else throw("Strict value is not equal to itself.") | |
521 | + | } | |
522 | + | else throw("Strict value is not equal to itself.") | |
523 | + | } | |
524 | + | else throw("Strict value is not equal to itself.") | |
525 | + | } | |
526 | + | else throw("Strict value is not equal to itself.") | |
527 | + | } | |
528 | + | else throw("Strict value is not equal to itself.") | |
529 | + | } | |
530 | + | else throw("Strict value is not equal to itself.") | |
531 | + | } | |
532 | + | else throw("Strict value is not equal to itself.") | |
533 | + | } | |
534 | + | ||
535 | + | ||
536 | + | ||
537 | + | @Callable(i) | |
538 | + | func startAltChain (blockHashHex,referenceHex,vrf,e2cTransfersRootHashHex,lastC2ETransferIndex) = { | |
539 | + | let checkBlockHash = validateBlockHash(blockHashHex) | |
540 | + | if ((checkBlockHash == checkBlockHash)) | |
541 | + | then { | |
542 | + | let checkEpoch = ensureCorrectEpoch(vrf) | |
543 | + | if ((checkEpoch == checkEpoch)) | |
544 | + | then { | |
545 | + | let $t01613216245 = blockMeta(referenceHex) | |
546 | + | let refChainHeight = $t01613216245._1 | |
547 | + | let refEpoch = $t01613216245._2 | |
548 | + | let refIgnored1 = $t01613216245._3 | |
549 | + | let refIgnored2 = $t01613216245._4 | |
550 | + | let refIgnored3 = $t01613216245._5 | |
551 | + | let refTransferIndex = $t01613216245._6 | |
552 | + | let finalizedEpoch = blockMeta(getStringValue(finalizedBlockKey))._2 | |
553 | + | let epochRef = if ((refEpoch >= finalizedEpoch)) | |
554 | + | then refEpoch | |
555 | + | else throw((((("Can not start alt chain from epoch " + toString(refEpoch)) + ", epoch ") + toString(finalizedEpoch)) + " is finalized")) | |
556 | + | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, mainChainId, referenceHex) | |
557 | + | if ((checkChain == checkChain)) | |
558 | + | then { | |
559 | + | let checkTransfers = ensureCorrectTransfers(refTransferIndex, lastC2ETransferIndex, true) | |
560 | + | if ((checkTransfers == checkTransfers)) | |
561 | + | then { | |
562 | + | let newChainId = (valueOrElse(getInteger(lastChainIdKey), 0) + 1) | |
563 | + | let newChainHeight = (refChainHeight + 1) | |
564 | + | let thisEpochMeta = match epochMeta(height) { | |
565 | + | case _: Unit => | |
566 | + | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(epochRef)) + SEP) + blockHashHex)) | |
567 | + | case other => | |
568 | + | throw("Epoch already started") | |
569 | + | } | |
570 | + | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
571 | + | if ((checkGenerator == checkGenerator)) | |
572 | + | then [thisEpochMeta, mkBlockMetaEntry(blockHashHex, newChainHeight, referenceHex, newChainId, e2cTransfersRootHashHex, lastC2ETransferIndex), StringEntry(chainFirstBlockIdKey(newChainId), blockHashHex), mkChainMetaEntry(newChainId, newChainHeight, blockHashHex), IntegerEntry(minerChainIdKey(i.originCaller), newChainId), IntegerEntry(chainLastHeightKey(newChainId, i.originCaller), newChainHeight), IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), newChainHeight), StringEntry(supportersKey(newChainId), toString(i.originCaller)), IntegerEntry(lastChainIdKey, newChainId)] | |
573 | + | else throw("Strict value is not equal to itself.") | |
574 | + | } | |
575 | + | else throw("Strict value is not equal to itself.") | |
576 | + | } | |
577 | + | else throw("Strict value is not equal to itself.") | |
578 | + | } | |
579 | + | else throw("Strict value is not equal to itself.") | |
580 | + | } | |
581 | + | else throw("Strict value is not equal to itself.") | |
582 | + | } | |
583 | + | ||
584 | + | ||
585 | + | ||
586 | + | @Callable(i) | |
587 | + | func extendAltChain (blockHashHex,referenceHex,vrf,chainId,e2cTransfersRootHashHex,lastC2ETransferIndex) = { | |
588 | + | let checkBlockHash = validateBlockHash(blockHashHex) | |
589 | + | if ((checkBlockHash == checkBlockHash)) | |
590 | + | then { | |
591 | + | let checkEpoch = ensureCorrectEpoch(vrf) | |
592 | + | if ((checkEpoch == checkEpoch)) | |
593 | + | then { | |
594 | + | let chainFirstBlockMeta = blockMeta(getStringValue(chainFirstBlockIdKey(chainId))) | |
595 | + | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, chainId, toBase16String(chainFirstBlockMeta._3)) | |
596 | + | if ((checkChain == checkChain)) | |
597 | + | then { | |
598 | + | let $t01855818612 = chainMeta(chainId) | |
599 | + | let chainHeight = $t01855818612._1 | |
600 | + | let chainLastBlock = $t01855818612._2 | |
601 | + | let checkReference = isReferenceCorrect(referenceHex, chainLastBlock) | |
602 | + | if ((checkReference == checkReference)) | |
603 | + | then { | |
604 | + | let checkTransfers = ensureCorrectTransfers(blockMeta(referenceHex)._6, lastC2ETransferIndex, true) | |
605 | + | if ((checkTransfers == checkTransfers)) | |
606 | + | then { | |
607 | + | let newChainHeight = (chainHeight + 1) | |
608 | + | let prevEpoch = blockMeta(referenceHex)._2 | |
609 | + | let updateMainChainData = if ((supportingBalance(chainId) > (computedTotalBalance / 2))) | |
610 | + | then { | |
611 | + | let lastChainId = valueOrElse(getInteger(lastChainIdKey), 0) | |
612 | + | let updateFinalizedBlock = getUpdateFinalizedBlockAction(i.originCaller, blockHashHex, prevEpoch) | |
613 | + | ([IntegerEntry(chainForkedHeightKey(mainChainId), chainFirstBlockMeta._1), IntegerEntry(mainChainIdKey, chainId), IntegerEntry(firstValidAltChainIdKey, (lastChainId + 1))] ++ updateFinalizedBlock) | |
614 | + | } | |
615 | + | else nil | |
616 | + | let thisEpochMeta = match epochMeta(height) { | |
617 | + | case _: Unit => | |
618 | + | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(prevEpoch)) + SEP) + blockHashHex)) | |
619 | + | case other => | |
620 | + | throw("Epoch already started") | |
621 | + | } | |
622 | + | if ((thisEpochMeta == thisEpochMeta)) | |
623 | + | then { | |
624 | + | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
625 | + | if ((checkGenerator == checkGenerator)) | |
626 | + | then { | |
627 | + | let updateMainChainLastMinedBlock = if (if ((updateMainChainData == nil)) | |
628 | + | then (valueOrElse(minerChainId(i.originCaller), 0) != chainId) | |
629 | + | else false) | |
630 | + | then [IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), chainFirstBlockMeta._1)] | |
631 | + | else nil | |
632 | + | ((([mkBlockMetaEntry(blockHashHex, newChainHeight, referenceHex, chainId, e2cTransfersRootHashHex, lastC2ETransferIndex), mkChainMetaEntry(chainId, newChainHeight, blockHashHex), thisEpochMeta, IntegerEntry(minerChainIdKey(i.originCaller), chainId), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight)] ++ updateMainChainData) ++ addSupporter(chainId, i.originCaller)) ++ updateMainChainLastMinedBlock) | |
633 | + | } | |
634 | + | else throw("Strict value is not equal to itself.") | |
635 | + | } | |
636 | + | else throw("Strict value is not equal to itself.") | |
637 | + | } | |
638 | + | else throw("Strict value is not equal to itself.") | |
639 | + | } | |
640 | + | else throw("Strict value is not equal to itself.") | |
641 | + | } | |
642 | + | else throw("Strict value is not equal to itself.") | |
643 | + | } | |
644 | + | else throw("Strict value is not equal to itself.") | |
645 | + | } | |
646 | + | else throw("Strict value is not equal to itself.") | |
647 | + | } | |
648 | + | ||
649 | + | ||
650 | + | ||
651 | + | @Callable(i) | |
652 | + | func appendBlock (blockHashHex,referenceHex,e2cTransfersRootHashHex,lastC2ETransferIndex) = { | |
653 | + | let checkCaller = if ((thisEpochMiner == i.originCaller)) | |
654 | + | then true | |
655 | + | else match thisEpochMiner { | |
656 | + | case epochMiner: Address => | |
657 | + | throw(("not allowed to forge blocks in this epoch, expected from " + toString(epochMiner))) | |
658 | + | case _ => | |
659 | + | throw("not allowed to forge blocks in this epoch, epoch miner is absent") | |
660 | + | } | |
661 | + | if ((checkCaller == checkCaller)) | |
662 | + | then { | |
663 | + | let chainId = valueOrElse(minerChainId(i.originCaller), mainChainId) | |
664 | + | let $t02096121012 = chainMeta(chainId) | |
665 | + | let chainHeight = $t02096121012._1 | |
666 | + | let lastBlockId = $t02096121012._2 | |
667 | + | let checkReference = isReferenceCorrect(referenceHex, lastBlockId) | |
668 | + | if ((checkReference == checkReference)) | |
669 | + | then { | |
670 | + | let checkTransfers = ensureCorrectTransfers(blockMeta(referenceHex)._6, lastC2ETransferIndex, false) | |
671 | + | if ((checkTransfers == checkTransfers)) | |
672 | + | then { | |
673 | + | let newChainHeight = (chainHeight + 1) | |
674 | + | let checkBlockHash = validateBlockHash(blockHashHex) | |
675 | + | if ((checkBlockHash == checkBlockHash)) | |
676 | + | then [mkBlockMetaEntry(blockHashHex, newChainHeight, lastBlockId, chainId, e2cTransfersRootHashHex, lastC2ETransferIndex), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight), mkChainMetaEntry(chainId, newChainHeight, blockHashHex), StringEntry(epochMetaKey(height), ((((toString(value(thisEpochMiner)) + SEP) + toString(thisEpochRef)) + SEP) + blockHashHex))] | |
677 | + | else throw("Strict value is not equal to itself.") | |
678 | + | } | |
679 | + | else throw("Strict value is not equal to itself.") | |
680 | + | } | |
681 | + | else throw("Strict value is not equal to itself.") | |
682 | + | } | |
683 | + | else throw("Strict value is not equal to itself.") | |
684 | + | } | |
685 | + | ||
686 | + | ||
687 | + | ||
688 | + | @Callable(i) | |
689 | + | func join (rewardAddress) = { | |
690 | + | func ensureNotOverrideOtherMinerPk (elAddressHex) = match getBinary(minerPkKey(elAddressHex)) { | |
691 | + | case pk: ByteVector => | |
692 | + | if ((pk == i.originCallerPublicKey)) | |
693 | + | then unit | |
694 | + | else throw(((("EL miner address " + elAddressHex) + " is already linked with ") + toBase58String(pk))) | |
695 | + | case _ => | |
696 | + | unit | |
697 | + | } | |
698 | + | ||
699 | + | if (!(isContractSetup())) | |
700 | + | then throw("The contract has not yet set up") | |
701 | + | else if ((MIN_BALANCE > wavesBalance(i.originCaller).generating)) | |
702 | + | then throw(((("Insufficient generating balance: " + toString(wavesBalance(i.originCaller).generating)) + ". Required: ") + toString(MIN_BALANCE))) | |
703 | + | else if ((size(rewardAddress) != 20)) | |
704 | + | then throw("rewardAddress should be an L2 address") | |
705 | + | else if ((size(allMiners) >= 50)) | |
706 | + | then throw("too many miners") | |
707 | + | else { | |
708 | + | func checkExistence (exists,miner) = if (exists) | |
709 | + | then true | |
710 | + | else (miner == toString(i.originCaller)) | |
711 | + | ||
712 | + | let alreadyExists = { | |
713 | + | let $l = allMiners | |
714 | + | let $s = size($l) | |
715 | + | let $acc0 = false | |
716 | + | func $f0_1 ($a,$i) = if (($i >= $s)) | |
717 | + | then $a | |
718 | + | else checkExistence($a, $l[$i]) | |
719 | + | ||
720 | + | func $f0_2 ($a,$i) = if (($i >= $s)) | |
721 | + | then $a | |
722 | + | else throw("List size exceeds 50") | |
723 | + | ||
724 | + | $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10), 11), 12), 13), 14), 15), 16), 17), 18), 19), 20), 21), 22), 23), 24), 25), 26), 27), 28), 29), 30), 31), 32), 33), 34), 35), 36), 37), 38), 39), 40), 41), 42), 43), 44), 45), 46), 47), 48), 49), 50) | |
725 | + | } | |
726 | + | if (alreadyExists) | |
727 | + | then nil | |
728 | + | else { | |
729 | + | let newMiner = toString(i.originCaller) | |
730 | + | let rewardAddressHex = toBase16String(rewardAddress) | |
731 | + | let check = ensureNotOverrideOtherMinerPk(rewardAddressHex) | |
732 | + | if ((check == check)) | |
733 | + | then { | |
734 | + | let newMiners = if ((size(allMiners) == 0)) | |
735 | + | then newMiner | |
736 | + | else ((allMinersStr + SEP) + newMiner) | |
737 | + | let deletePrevRewardAddressPk = match getString(minerRewardAddressKey(newMiner)) { | |
738 | + | case prevAddress: String => | |
739 | + | if ((prevAddress == toBase16String(rewardAddress))) | |
740 | + | then nil | |
741 | + | else [DeleteEntry(minerPkKey(prevAddress))] | |
742 | + | case _ => | |
743 | + | nil | |
744 | + | } | |
745 | + | ([StringEntry(allMinersKey, newMiners), StringEntry(minerRewardAddressKey(newMiner), ("0x" + rewardAddressHex)), BinaryEntry(minerPkKey(rewardAddressHex), i.originCallerPublicKey)] ++ deletePrevRewardAddressPk) | |
746 | + | } | |
747 | + | else throw("Strict value is not equal to itself.") | |
748 | + | } | |
749 | + | } | |
750 | + | } | |
751 | + | ||
752 | + | ||
753 | + | ||
754 | + | @Callable(i) | |
755 | + | func leave () = { | |
756 | + | let leavingMiner = toString(i.originCaller) | |
757 | + | func skipLeavingMiner (acc,miner) = if ((miner == leavingMiner)) | |
758 | + | then acc | |
759 | + | else (acc :+ miner) | |
760 | + | ||
761 | + | let remainingMiners = { | |
762 | + | let $l = allMiners | |
763 | + | let $s = size($l) | |
764 | + | let $acc0 = nil | |
765 | + | func $f0_1 ($a,$i) = if (($i >= $s)) | |
766 | + | then $a | |
767 | + | else skipLeavingMiner($a, $l[$i]) | |
768 | + | ||
769 | + | func $f0_2 ($a,$i) = if (($i >= $s)) | |
770 | + | then $a | |
771 | + | else throw("List size exceeds 50") | |
772 | + | ||
773 | + | $f0_2($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($acc0, 0), 1), 2), 3), 4), 5), 6), 7), 8), 9), 10), 11), 12), 13), 14), 15), 16), 17), 18), 19), 20), 21), 22), 23), 24), 25), 26), 27), 28), 29), 30), 31), 32), 33), 34), 35), 36), 37), 38), 39), 40), 41), 42), 43), 44), 45), 46), 47), 48), 49), 50) | |
774 | + | } | |
775 | + | let rewardAddrKey = minerRewardAddressKey(leavingMiner) | |
776 | + | let prevRewardAddress = valueOrErrorMessage(getString(this, rewardAddrKey), "miner has never joined") | |
777 | + | if ((prevRewardAddress == prevRewardAddress)) | |
778 | + | then if ((thisEpochMiner == i.originCaller)) | |
779 | + | then throw("designated miner can't leave") | |
780 | + | else [StringEntry(allMinersKey, makeString_2C(remainingMiners, SEP))] | |
781 | + | else throw("Strict value is not equal to itself.") | |
782 | + | } | |
783 | + | ||
784 | + | ||
785 | + | ||
786 | + | @Callable(i) | |
787 | + | func transfer (destElAddressHex) = { | |
788 | + | let checkAddress = if ((size(destElAddressHex) == ETH_ADDRESS_STR_SIZE)) | |
789 | + | then fromBase16String(destElAddressHex) | |
790 | + | else throw(((("Address should have " + toString(ETH_ADDRESS_STR_SIZE)) + " characters, got ") + toString(size(destElAddressHex)))) | |
791 | + | if ((checkAddress == checkAddress)) | |
792 | + | then { | |
793 | + | let checkPayment = if ((size(i.payments) == 1)) | |
794 | + | then true | |
795 | + | else throw(("Expected one payment as a transfer amount, got " + toString(size(i.payments)))) | |
796 | + | if ((checkPayment == checkPayment)) | |
797 | + | then { | |
798 | + | let tokenIdStr = getStringValue(tokenIdKey) | |
799 | + | let tokenId = fromBase58String(tokenIdStr) | |
800 | + | let t = i.payments[0] | |
801 | + | let checkAssetId = match t.assetId { | |
802 | + | case assetId: ByteVector => | |
803 | + | if ((assetId == tokenId)) | |
804 | + | then true | |
805 | + | else throw(((("Expected " + tokenIdStr) + " in the payment, got ") + toBase58String(assetId))) | |
806 | + | case _ => | |
807 | + | throw((("Expected " + tokenIdStr) + " in the payment, got Waves")) | |
808 | + | } | |
809 | + | if ((checkAssetId == checkAssetId)) | |
810 | + | then { | |
811 | + | let lastIndex = blockMeta(mainChainLastBlock)._6 | |
812 | + | let queueSize = (nativeTransfersCount - lastIndex) | |
813 | + | let checkQueueLimit = if ((160 > queueSize)) | |
814 | + | then amountGtEq(t, 1000000, queueSize) | |
815 | + | else if ((1600 > queueSize)) | |
816 | + | then amountGtEq(t, 10000000, queueSize) | |
817 | + | else if ((3200 > queueSize)) | |
818 | + | then amountGtEq(t, 100000000, queueSize) | |
819 | + | else if ((6400 > queueSize)) | |
820 | + | then amountGtEq(t, 1000000000, queueSize) | |
821 | + | else throw((("Transfers denied for queue size of " + toString(queueSize)) + ". Wait until current transfers processed")) | |
822 | + | if ((checkQueueLimit == checkQueueLimit)) | |
823 | + | then [IntegerEntry(nativeTransfersCountKey, (nativeTransfersCount + 1)), mkNativeTransferEntry(nativeTransfersCount, destElAddressHex, t.amount), Burn(tokenId, t.amount)] | |
824 | + | else throw("Strict value is not equal to itself.") | |
825 | + | } | |
826 | + | else throw("Strict value is not equal to itself.") | |
827 | + | } | |
828 | + | else throw("Strict value is not equal to itself.") | |
829 | + | } | |
830 | + | else throw("Strict value is not equal to itself.") | |
831 | + | } | |
832 | + | ||
833 | + | ||
834 | + | ||
835 | + | @Callable(i) | |
836 | + | func withdraw (blockHashHex,merkleProof,transferIndexInBlock,amount) = { | |
837 | + | let withdrawBlockMeta = blockMeta(blockHashHex) | |
838 | + | let withdrawBlockHeight = withdrawBlockMeta._1 | |
839 | + | let finalizedBlockHeight = blockMeta(getStringValue(finalizedBlockKey))._1 | |
840 | + | let mainChainLastBlockHeight = blockMeta(mainChainLastBlock)._1 | |
841 | + | if ((withdrawBlockHeight > finalizedBlockHeight)) | |
842 | + | then throw(((("EL block #" + toString(withdrawBlockHeight)) + " is not finalized. The current finalized is #") + toString(finalizedBlockHeight))) | |
843 | + | else if ((size(merkleProof) != WITHDRAW_PROOFS_SIZE)) | |
844 | + | then throw(((("Expected " + toString(WITHDRAW_PROOFS_SIZE)) + " proofs, got ") + toString(size(merkleProof)))) | |
845 | + | else if ((0 > transferIndexInBlock)) | |
846 | + | then throw(("Transfer index in block should be nonnegative, got " + toString(transferIndexInBlock))) | |
847 | + | else if ((0 >= amount)) | |
848 | + | then throw(("Amount should be positive, got " + toString(amount))) | |
849 | + | else { | |
850 | + | let withdrawBlockChainId = withdrawBlockMeta._4 | |
851 | + | let isMainChain = (withdrawBlockChainId == mainChainId) | |
852 | + | let relatesToMainChain = match getInteger(chainForkedHeightKey(withdrawBlockChainId)) { | |
853 | + | case forkedHeight: Int => | |
854 | + | (forkedHeight > withdrawBlockHeight) | |
855 | + | case _ => | |
856 | + | throw((((blockHashHex + " is on an alternative chain #") + toString(withdrawBlockChainId)) + " that was not approved by majority. Wait for some blocks")) | |
857 | + | } | |
858 | + | if (if (isMainChain) | |
859 | + | then true | |
860 | + | else relatesToMainChain) | |
861 | + | then { | |
862 | + | let recipient = i.originCaller | |
863 | + | let recipientPkHash = take(drop(recipient.bytes, 2), PUBLIC_KEY_HASH_SIZE) | |
864 | + | let zeroAmountBytes = base58'11111111111111111111111111111111111111111111' | |
865 | + | let amountBytes = toBytes(amount) | |
866 | + | let elEventData = ((recipientPkHash + take(zeroAmountBytes, (size(zeroAmountBytes) - size(amountBytes)))) + amountBytes) | |
867 | + | let elEventDataDigest = blake2b256_16Kb(elEventData) | |
868 | + | let calculatedRootHash = createMerkleRoot(merkleProof, elEventDataDigest, transferIndexInBlock) | |
869 | + | let expectedRootHash = withdrawBlockMeta._5 | |
870 | + | if ((calculatedRootHash == expectedRootHash)) | |
871 | + | then { | |
872 | + | let tokenId = fromBase58String(getStringValue(tokenIdKey)) | |
873 | + | let transfersKey = blockE2CTransfersKey(blockHashHex) | |
874 | + | [Reissue(tokenId, amount, true), ScriptTransfer(recipient, amount, tokenId), StringEntry(transfersKey, setOrFail(valueOrElse(getString(transfersKey), ""), transferIndexInBlock))] | |
875 | + | } | |
876 | + | else throw((((((("Expected root hash: " + toBase16String(expectedRootHash)) + ", got: ") + toBase16String(calculatedRootHash)) + ". Event data digest: ") + toBase64String(elEventDataDigest)) + ". Check your withdraw arguments")) | |
877 | + | } | |
878 | + | else throw((("Expected " + blockHashHex) + " to be either on the main chain or relate to it")) | |
879 | + | } | |
880 | + | } | |
881 | + | ||
882 | + | ||
883 | + | ||
884 | + | @Callable(i) | |
885 | + | func setup (genesisBlockHashHex,minerRewardInGwei) = if (isContractSetup()) | |
886 | + | then throw("The contract has been already set up") | |
887 | + | else if ((0 > minerRewardInGwei)) | |
888 | + | then throw("The miner reward must be nonnegative") | |
889 | + | else { | |
890 | + | let genesisBlockHash = fromBase16String(genesisBlockHashHex) | |
891 | + | let checkGenesisBlockHashSize = if ((size(genesisBlockHash) == BLOCK_HASH_SIZE)) | |
892 | + | then true | |
893 | + | else throw("Wrong genesis block hash") | |
894 | + | if ((checkGenesisBlockHashSize == checkGenesisBlockHashSize)) | |
895 | + | then { | |
896 | + | let emptyPk = base58'11111111111111111111111111111111' | |
897 | + | let genesisMinerAddress = addressFromPublicKey(emptyPk) | |
898 | + | let genesisEthRewardAddress = base58'11111111111111111111' | |
899 | + | let genesisBlockReferenceHash = "0000000000000000000000000000000000000000000000000000000000000000" | |
900 | + | let issue = Issue("UNIT0", "Native token", 0, 8, true) | |
901 | + | let tokenId = calculateAssetId(issue) | |
902 | + | let genesisBlockMeta = mkBlockMetaEntry(genesisBlockHashHex, 0, genesisBlockReferenceHash, 0, toBase16String(base58''), -1) | |
903 | + | [genesisBlockMeta, StringEntry(chainFirstBlockIdKey(0), genesisBlockHashHex), mkChainMetaEntry(0, 0, genesisBlockHashHex), IntegerEntry(minerRewardKey, minerRewardInGwei), StringEntry(epochMetaKey(height), ((toString(genesisMinerAddress) + ",0,") + genesisBlockHashHex)), StringEntry(finalizedBlockKey, genesisBlockHashHex), issue, StringEntry(tokenIdKey, toBase58String(tokenId)), StringEntry(elBridgeAddressKey, "0x0000000000000000000000000000000000006a7e")] | |
904 | + | } | |
905 | + | else throw("Strict value is not equal to itself.") | |
906 | + | } | |
907 | + | ||
908 | + |
github/deemru/w8io/026f985 42.86 ms ◑