tx · Eh7NKbyaoxgEJ4BwqmwtiBQho4gcdExwKQ9ZwLmSAcYj 3MsqKJ6o1ABE37676cHHBxJRs6huYTt72ch: -0.04300000 Waves 2024.08.20 11:59 [3246951] smart account 3MsqKJ6o1ABE37676cHHBxJRs6huYTt72ch > SELF 0.00000000 Waves
{ "type": 13, "id": "Eh7NKbyaoxgEJ4BwqmwtiBQho4gcdExwKQ9ZwLmSAcYj", "fee": 4300000, "feeAssetId": null, "timestamp": 1724144418922, "version": 2, "chainId": 84, "sender": "3MsqKJ6o1ABE37676cHHBxJRs6huYTt72ch", "senderPublicKey": "3xjN6fjYDXBGUE1mcRw2Fvr4R6tEZnuJA98QFGF99sXd", "proofs": [ "3zWkSiTLgG23JszjYZkLjYmaQDS3Xm8DxV79CjhLECXM9eLCH4Wcj4m8DneGvc9nvhtsqemrZH3nL6kKxCTTkGEZ" ], "script": "base64:CAJiCAISBQoDCAgIEgYKBAgICAESBgoECAgBCBIHCgUICAEIARIGCgQICAEIEgcKBQgIAQgBEgcKBQEICAEIEggKBgEICAEIARIDCgECEgASAwoBCBIGCgQIEgEBEgYKBAgBCAhUAAdJTlRfTUFYAP//////////fwAFV0FWRVMAgMLXLwALTUlOX0JBTEFOQ0UJAGgCAKCcAQUFV0FWRVMAA1NFUAIBLAAPQkxPQ0tfSEFTSF9TSVpFACAADEFERFJFU1NfU0laRQAaABRQVUJMSUNfS0VZX0hBU0hfU0laRQAUAA5ST09UX0hBU0hfU0laRQAgABZNQVhfQ0xfVE9fRUxfVFJBTlNGRVJTABAACXplcm9lc1N0cgKACDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAEHRoaXNFcG9jaERhdGFLZXkCDXRoaXNFcG9jaERhdGEADGFsbE1pbmVyc0tleQIJYWxsTWluZXJzAA5tYWluQ2hhaW5JZEtleQILbWFpbkNoYWluSWQADmxhc3RDaGFpbklkS2V5AgtsYXN0Q2hhaW5JZAAXZmlyc3RWYWxpZEFsdENoYWluSWRLZXkCFGZpcnN0VmFsaWRBbHRDaGFpbklkAA5taW5lclJld2FyZEtleQILbWluZXJSZXdhcmQAGXN0YWtpbmdDb250cmFjdEFkZHJlc3NLZXkCFnN0YWtpbmdDb250cmFjdEFkZHJlc3MACmJsb2NrTWV0YUsCCGJsb2NrXzB4ABFmaW5hbGl6ZWRCbG9ja0tleQIOZmluYWxpemVkQmxvY2sACnRva2VuSWRLZXkCB3Rva2VuSWQAEmVsQnJpZGdlQWRkcmVzc0tleQIPZWxCcmlkZ2VBZGRyZXNzABduYXRpdmVUcmFuc2ZlcnNDb3VudEtleQIUbmF0aXZlVHJhbnNmZXJzQ291bnQAF2NsVG9FbFRyYW5zZmVyc0Vwb2NoS2V5AhRjbFRvRWxUcmFuc2ZlcnNFcG9jaAEDcGFkAQFpBAFzCQCkAwEFAWkEByRtYXRjaDAJALECAQUBcwMJAAACAAEFByRtYXRjaDAJAKwCAgIHMDAwMDAwMAUBcwMJAAACAAIFByRtYXRjaDAJAKwCAgIGMDAwMDAwBQFzAwkAAAIAAwUHJG1hdGNoMAkArAICAgUwMDAwMAUBcwMJAAACAAQFByRtYXRjaDAJAKwCAgIEMDAwMAUBcwMJAAACAAUFByRtYXRjaDAJAKwCAgIDMDAwBQFzAwkAAAIABgUHJG1hdGNoMAkArAICAgIwMAUBcwMJAAACAAcFByRtYXRjaDAJAKwCAgIBMAUBcwUBcwEXYmxvY2tFbFRvQ2xUcmFuc2ZlcnNLZXkBDGJsb2NrSGFzaEhleAkArAICAhJlbFRvQ2xUcmFuc2ZlcnNfMHgFDGJsb2NrSGFzaEhleAEMZXBvY2hNZXRhS2V5AQVlcG9jaAkArAICAgZlcG9jaF8JAQNwYWQBBQVlcG9jaAEUY2hhaW5GaXJzdEJsb2NrSWRLZXkBB2NoYWluSWQJAKwCAgkArAICAgVjaGFpbgkApAMBBQdjaGFpbklkAgpGaXJzdEJsb2NrAQxjaGFpbk1ldGFLZXkBB2NoYWluSWQJAKwCAgIGY2hhaW5fCQEDcGFkAQUHY2hhaW5JZAESY2hhaW5MYXN0SGVpZ2h0S2V5AgdjaGFpbklkBW1pbmVyCQCsAgIJAKwCAgkArAICAgZjaGFpbl8JAQNwYWQBBQdjaGFpbklkAgFfCQClCAEFBW1pbmVyARRjaGFpbkZvcmtlZEhlaWdodEtleQEHY2hhaW5JZAkArAICCQCsAgICBmNoYWluXwkBA3BhZAEFB2NoYWluSWQCDEZvcmtlZEhlaWdodAENc3VwcG9ydGVyc0tleQEHY2hhaW5JZAkArAICCQCsAgICBWNoYWluCQCkAwEFB2NoYWluSWQCClN1cHBvcnRlcnMBFW1pbmVyUmV3YXJkQWRkcmVzc0tleQEJbWluZXJBZGRyCQCsAgIJAKwCAgIGbWluZXJfBQltaW5lckFkZHICDl9SZXdhcmRBZGRyZXNzAQptaW5lclBrS2V5AQ1yZXdhcmRBZGRyZXNzCQCsAgIJAKwCAgIIbWluZXJfMHgFDXJld2FyZEFkZHJlc3MCA19QSwEPbWluZXJDaGFpbklkS2V5AQVtaW5lcgkArAICCQCsAgICBm1pbmVyXwkApQgBBQVtaW5lcgIIX0NoYWluSWQAFG5hdGl2ZVRyYW5zZmVyc0NvdW50CQELdmFsdWVPckVsc2UCCQCaCAIFBHRoaXMFF25hdGl2ZVRyYW5zZmVyc0NvdW50S2V5AAABEW5hdGl2ZVRyYW5zZmVyS2V5AQVpbmRleAkArAICAg9uYXRpdmVUcmFuc2Zlcl8JAKQDAQUFaW5kZXgBFW1rTmF0aXZlVHJhbnNmZXJFbnRyeQMFaW5kZXgQZGVzdEVsQWRkcmVzc0hleAZhbW91bnQJAQtTdHJpbmdFbnRyeQIJARFuYXRpdmVUcmFuc2ZlcktleQEFBWluZGV4CQCsAgIJAKwCAgkArAICAgIweAUQZGVzdEVsQWRkcmVzc0hleAUDU0VQCQCkAwEFBmFtb3VudAAUY2xUb0VsVHJhbnNmZXJzRXBvY2gJAQt2YWx1ZU9yRWxzZQIJAJoIAgUEdGhpcwUXY2xUb0VsVHJhbnNmZXJzRXBvY2hLZXkFB0lOVF9NQVgBHnJlcXVpcmVDbFRvRWxUcmFuc2ZlcnNEaXNhYmxlZAADCQBmAgUUY2xUb0VsVHJhbnNmZXJzRXBvY2gFBmhlaWdodAUEdW5pdAkAAgEJAKwCAgkArAICAidDTCB0byBFTCB0cmFuc2ZlcnMgYWN0aXZhdGVkIG9uIGVwb2NoICMJAKQDAQUGaGVpZ2h0AhUuIFVwZ3JhZGUgeW91ciBjbGllbnQBHXJlcXVpcmVDbFRvRWxUcmFuc2ZlcnNFbmFibGVkAAMJAGcCBQZoZWlnaHQFFGNsVG9FbFRyYW5zZmVyc0Vwb2NoBQR1bml0CQACAQkArAICCQCsAgIJAKwCAgIvQ0wgdG8gRUwgdHJhbnNmZXJzIGhhdmVuJ3QgYWN0aXZhdGVkIG9uIGVwb2NoICMJAKQDAQUGaGVpZ2h0AhouIFdhaXQgZm9yIGFjdGl2YXRpb24gb24gIwkApAMBBRRjbFRvRWxUcmFuc2ZlcnNFcG9jaAEWZW5zdXJlQ29ycmVjdFRyYW5zZmVycwMQcmVmVHJhbnNmZXJJbmRleA10cmFuc2ZlckluZGV4DGV4cGVjdFJld2FyZAQMbWF4VHJhbnNmZXJzAwUMZXhwZWN0UmV3YXJkCQBlAgUWTUFYX0NMX1RPX0VMX1RSQU5TRkVSUwABBRZNQVhfQ0xfVE9fRUxfVFJBTlNGRVJTBA9hY3R1YWxUcmFuc2ZlcnMJAGUCBQ10cmFuc2ZlckluZGV4BRByZWZUcmFuc2ZlckluZGV4BAtjaGVja051bWJlcgMJAGYCBQ9hY3R1YWxUcmFuc2ZlcnMFDG1heFRyYW5zZmVycwkAAgEJAKwCAgkArAICCQCsAgICDUFsbG93ZWQgb25seSAJAKQDAQUMbWF4VHJhbnNmZXJzAhAgdHJhbnNmZXJzLCBnb3QgCQCkAwEFD2FjdHVhbFRyYW5zZmVycwYDCQAAAgULY2hlY2tOdW1iZXIFC2NoZWNrTnVtYmVyAwkAZwIFDXRyYW5zZmVySW5kZXgFFG5hdGl2ZVRyYW5zZmVyc0NvdW50CQACAQkArAICCQCsAgIJAKwCAgIVQXR0ZW1wdCB0byB0cmFuc2ZlciAjCQCkAwEFDXRyYW5zZmVySW5kZXgCFy4gQXZhaWxhYmxlIHRyYW5zZmVyczogCQCkAwEFFG5hdGl2ZVRyYW5zZmVyc0NvdW50BgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgEKYW1vdW50R3RFcQMBdARndEVxCXF1ZXVlU2l6ZQMJAGcCCAUBdAZhbW91bnQFBGd0RXEGCQACAQkArAICCQCsAgIJAKwCAgkArAICCQCsAgIJAKwCAgIUVHJhbnNmZXJyaW5nIGFtb3VudCAJAKQDAQgFAXQGYW1vdW50Ag4gc2hvdWxkIGJlID49IAkApAMBBQRndEVxAhMgZm9yIHF1ZXVlIHNpemUgb2YgCQCkAwEFCXF1ZXVlU2l6ZQIXLiBUcmFuc2ZlciBtb3JlIG9yIHdhaXQAFnN0YWtpbmdDb250cmFjdEFkZHJlc3MEByRtYXRjaDAJAJ0IAgUEdGhpcwUZc3Rha2luZ0NvbnRyYWN0QWRkcmVzc0tleQMJAAECBQckbWF0Y2gwAgZTdHJpbmcEAXMFByRtYXRjaDAJARN2YWx1ZU9yRXJyb3JNZXNzYWdlAgkApggBBQFzCQCsAgICImludmFsaWQgc3Rha2luZyBjb250cmFjdCBhZGRyZXNzOiAFAXMJAQdBZGRyZXNzAQkBEUBleHRyTmF0aXZlKDEwNTIpAgUEdGhpcwUZc3Rha2luZ0NvbnRyYWN0QWRkcmVzc0tleQERZ2VuZXJhdGluZ0JhbGFuY2UBB2FkZHJlc3MEByRtYXRjaDAJAJ0IAgUWc3Rha2luZ0NvbnRyYWN0QWRkcmVzcwkArAICAgQlc19fCQClCAEFB2FkZHJlc3MDCQABAgUHJG1hdGNoMAIGU3RyaW5nBANzdHIFByRtYXRjaDAECXBhcmFtTGlzdAkAtQkCBQNzdHICAl9fBApwcmV2SGVpZ2h0CQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUJcGFyYW1MaXN0AAEEC3ByZXZCYWxhbmNlCQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUJcGFyYW1MaXN0AAIECm5leHRIZWlnaHQJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQlwYXJhbUxpc3QAAwQLbmV4dEJhbGFuY2UJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQlwYXJhbUxpc3QABAMJAGcCBQZoZWlnaHQFCm5leHRIZWlnaHQFC25leHRCYWxhbmNlAwkAZwIFBmhlaWdodAUKcHJldkhlaWdodAULcHJldkJhbGFuY2UAAAAAAQljaGFpbk1ldGEBB2NoYWluSWQEAXMJARFAZXh0ck5hdGl2ZSgxMDU4KQEJAQxjaGFpbk1ldGFLZXkBBQdjaGFpbklkBAVpdGVtcwkAtQkCBQFzBQNTRVAJAJQKAgkBDXBhcnNlSW50VmFsdWUBCQCRAwIFBWl0ZW1zAAAJAJEDAgUFaXRlbXMAAQEQbWtDaGFpbk1ldGFFbnRyeQMHY2hhaW5JZA5uZXdDaGFpbkhlaWdodAxibG9ja0hhc2hIZXgJAQtTdHJpbmdFbnRyeQIJAQxjaGFpbk1ldGFLZXkBBQdjaGFpbklkCQCsAgIJAKwCAgkApAMBBQ5uZXdDaGFpbkhlaWdodAUDU0VQBQxibG9ja0hhc2hIZXgAC21haW5DaGFpbklkCQELdmFsdWVPckVsc2UCCQCfCAEFDm1haW5DaGFpbklkS2V5AAAACyR0MDY0MjM2NDg5CQEJY2hhaW5NZXRhAQULbWFpbkNoYWluSWQAD21haW5DaGFpbkhlaWdodAgFCyR0MDY0MjM2NDg5Al8xABJtYWluQ2hhaW5MYXN0QmxvY2sIBQskdDA2NDIzNjQ4OQJfMgEJZXBvY2hNZXRhAQVlcG9jaAQHJG1hdGNoMAkAoggBCQEMZXBvY2hNZXRhS2V5AQUFZXBvY2gDCQABAgUHJG1hdGNoMAIGU3RyaW5nBAFzBQckbWF0Y2gwBAlmcmFnbWVudHMJALUJAgUBcwUDU0VQCQCVCgMJARFAZXh0ck5hdGl2ZSgxMDYyKQEJAJEDAgUJZnJhZ21lbnRzAAAJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQlmcmFnbWVudHMAAQkAkQMCBQlmcmFnbWVudHMAAgUEdW5pdAALJHQwNjcyMDcxODQEByRtYXRjaDAJAQllcG9jaE1ldGEBBQZoZWlnaHQDCQABAgUHJG1hdGNoMAIWKEFkZHJlc3MsIEludCwgU3RyaW5nKQQBbQUHJG1hdGNoMAUBbQQHJG1hdGNoMQkAoggBBRB0aGlzRXBvY2hEYXRhS2V5AwkAAQIFByRtYXRjaDECBlN0cmluZwQQcmF3VGhpc0Vwb2NoRGF0YQUHJG1hdGNoMQQNdGhpc0Vwb2NoRGF0YQkAtQkCBRByYXdUaGlzRXBvY2hEYXRhBQNTRVAECXRoaXNFcG9jaAkBDXBhcnNlSW50VmFsdWUBCQCRAwIFDXRoaXNFcG9jaERhdGEAAAkAlQoDAwkAAAIFCXRoaXNFcG9jaAUGaGVpZ2h0CQERQGV4dHJOYXRpdmUoMTA2MikBCQCRAwIFDXRoaXNFcG9jaERhdGEAAQUEdW5pdAAAAgAJAJUKAwUEdW5pdAAAAgAADnRoaXNFcG9jaE1pbmVyCAULJHQwNjcyMDcxODQCXzEADHRoaXNFcG9jaFJlZggFCyR0MDY3MjA3MTg0Al8yABJ0aGlzRXBvY2hMYXN0QmxvY2sIBQskdDA2NzIwNzE4NAJfMwAMYWxsTWluZXJzU3RyCQELdmFsdWVPckVsc2UCCQCiCAEFDGFsbE1pbmVyc0tleQIAAAlhbGxNaW5lcnMEByRtYXRjaDAFDGFsbE1pbmVyc1N0cgMJAAACAgAFByRtYXRjaDAFA25pbAMJAAECBQckbWF0Y2gwAgZTdHJpbmcEA3JhdwUHJG1hdGNoMAkAvAkCBQNyYXcFA1NFUAkAAgECC01hdGNoIGVycm9yAQlibG9ja01ldGEBB2Jsb2NrSWQEBG1ldGEJARFAZXh0ck5hdGl2ZSgxMDU3KQEJAKwCAgUKYmxvY2tNZXRhSwUHYmxvY2tJZAQIbWV0YVNpemUJAMgBAQUEbWV0YQQLYmxvY2tIZWlnaHQJALEJAQUEbWV0YQQKYmxvY2tFcG9jaAkAsgkCBQRtZXRhAAgEC2Jsb2NrUGFyZW50CQDJAQIJAMoBAgUEbWV0YQAQBQ9CTE9DS19IQVNIX1NJWkUEDmJsb2NrR2VuZXJhdG9yCQDJAQIJAMoBAgUEbWV0YQkAZAIAEAUPQkxPQ0tfSEFTSF9TSVpFBQxBRERSRVNTX1NJWkUEB2NoYWluSWQJALIJAgUEbWV0YQkAZAIJAGQCABAFD0JMT0NLX0hBU0hfU0laRQUMQUREUkVTU19TSVpFBApiYXNlT2Zmc2V0CQBkAgkAZAIAGAUPQkxPQ0tfSEFTSF9TSVpFBQxBRERSRVNTX1NJWkUEDnJlbWFpbmluZ0J5dGVzCQBlAgUIbWV0YVNpemUFCmJhc2VPZmZzZXQEF2VsVG9DbFRyYW5zZmVyc1Jvb3RIYXNoAwkAZwIFDnJlbWFpbmluZ0J5dGVzBQ5ST09UX0hBU0hfU0laRQkAyQECCQDKAQIFBG1ldGEFCmJhc2VPZmZzZXQFDlJPT1RfSEFTSF9TSVpFAQAEF2xhc3RDbFRvRWxUcmFuc2ZlckluZGV4AwMJAAACBQ5yZW1haW5pbmdCeXRlcwAIBgkAZgIFDnJlbWFpbmluZ0J5dGVzBQ5ST09UX0hBU0hfU0laRQkAsgkCBQRtZXRhCQBkAgUKYmFzZU9mZnNldAkAyAEBBRdlbFRvQ2xUcmFuc2ZlcnNSb290SGFzaAD///////////8BCQCZCgcFC2Jsb2NrSGVpZ2h0BQpibG9ja0Vwb2NoBQtibG9ja1BhcmVudAUOYmxvY2tHZW5lcmF0b3IFB2NoYWluSWQFF2VsVG9DbFRyYW5zZmVyc1Jvb3RIYXNoBRdsYXN0Q2xUb0VsVHJhbnNmZXJJbmRleAEQbWtCbG9ja01ldGFFbnRyeQcMYmxvY2tIYXNoSGV4C2Jsb2NrSGVpZ2h0DmJsb2NrUGFyZW50SGV4DmJsb2NrR2VuZXJhdG9yB2NoYWluSWQaZWxUb0NsVHJhbnNmZXJzUm9vdEhhc2hIZXgXbGFzdENsVG9FbFRyYW5zZmVySW5kZXgED2Jsb2NrRXBvY2hCeXRlcwkAmgMBBQZoZWlnaHQED2Jsb2NrTWV0YUJ5dGVzMQkAywECCQDLAQIJAMsBAgkAywECCQDLAQIJAJoDAQULYmxvY2tIZWlnaHQFD2Jsb2NrRXBvY2hCeXRlcwkA3QQBBQ5ibG9ja1BhcmVudEhleAgFDmJsb2NrR2VuZXJhdG9yBWJ5dGVzCQCaAwEFB2NoYWluSWQJAN0EAQUaZWxUb0NsVHJhbnNmZXJzUm9vdEhhc2hIZXgED2Jsb2NrTWV0YUJ5dGVzMgMJAGYCBRRjbFRvRWxUcmFuc2ZlcnNFcG9jaAUGaGVpZ2h0AQAJAJoDAQUXbGFzdENsVG9FbFRyYW5zZmVySW5kZXgJAQtCaW5hcnlFbnRyeQIJAKwCAgUKYmxvY2tNZXRhSwUMYmxvY2tIYXNoSGV4CQDLAQIFD2Jsb2NrTWV0YUJ5dGVzMQUPYmxvY2tNZXRhQnl0ZXMyAQxsYXN0SGVpZ2h0QnkCBW1pbmVyB2NoYWluSWQEByRtYXRjaDAJAJ8IAQkBEmNoYWluTGFzdEhlaWdodEtleQIFB2NoYWluSWQFBW1pbmVyAwkAAQIFByRtYXRjaDACA0ludAQBaAUHJG1hdGNoMAUBaAQJYmxvY2tIYXNoCQERQGV4dHJOYXRpdmUoMTA1OCkBCQCsAgIJAKwCAgkArAICAgVjaGFpbgkApAMBBQdjaGFpbklkAgtMYXN0TWluZWRCeQkApQgBBQVtaW5lcggJAQlibG9ja01ldGEBBQlibG9ja0hhc2gCXzEADCR0MDkyNDYxMDIxMgQJaGl0U291cmNlBAckbWF0Y2gwCAUJbGFzdEJsb2NrA3ZyZgMJAAECBQckbWF0Y2gwAgpCeXRlVmVjdG9yBAN2cmYFByRtYXRjaDAFA3ZyZggFCWxhc3RCbG9jaxNnZW5lcmF0aW9uU2lnbmF0dXJlCgEMcHJvY2Vzc01pbmVyAgRwcmV2BW1pbmVyBAskdDA5NTQ0OTYwNwUEcHJldgQJcHJldkRlbGF5CAULJHQwOTU0NDk2MDcCXzEECXByZXZNaW5lcggFCyR0MDk1NDQ5NjA3Al8yBBBwcmV2VG90YWxCYWxhbmNlCAULJHQwOTU0NDk2MDcCXzMECnByZXZNaW5lcnMIBQskdDA5NTQ0OTYwNwJfNAQMbWluZXJBZGRyZXNzCQERQGV4dHJOYXRpdmUoMTA2MikBBQVtaW5lcgQPd2F2ZXNHZW5CYWxhbmNlCAkA7wcBBQxtaW5lckFkZHJlc3MKZ2VuZXJhdGluZwQMbWluZXJCYWxhbmNlCQERZ2VuZXJhdGluZ0JhbGFuY2UBBQxtaW5lckFkZHJlc3MDAwkAZgIFC01JTl9CQUxBTkNFBQ93YXZlc0dlbkJhbGFuY2UGCQBnAgAABQxtaW5lckJhbGFuY2UFBHByZXYECW5leHREZWxheQkAhQcCBQxtaW5lckFkZHJlc3MFDG1pbmVyQmFsYW5jZQMJAGYCBQlwcmV2RGVsYXkFCW5leHREZWxheQkAlgoEBQluZXh0RGVsYXkFBW1pbmVyCQBkAgUQcHJldlRvdGFsQmFsYW5jZQUMbWluZXJCYWxhbmNlCQDNCAIFCnByZXZNaW5lcnMFBW1pbmVyCQCWCgQFCXByZXZEZWxheQUJcHJldk1pbmVyCQBkAgUQcHJldlRvdGFsQmFsYW5jZQUMbWluZXJCYWxhbmNlCQDNCAIFCnByZXZNaW5lcnMFBW1pbmVyCgACJGwFCWFsbE1pbmVycwoAAiRzCQCQAwEFAiRsCgAFJGFjYzAJAJYKBAUHSU5UX01BWAIAAAAFA25pbAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEMcHJvY2Vzc01pbmVyAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhRMaXN0IHNpemUgZXhjZWVkcyA1MAkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIADWNvbXB1dGVkRGVsYXkIBQwkdDA5MjQ2MTAyMTICXzEAEWNvbXB1dGVkR2VuZXJhdG9yCAUMJHQwOTI0NjEwMjEyAl8yABRjb21wdXRlZFRvdGFsQmFsYW5jZQgFDCR0MDkyNDYxMDIxMgJfMwAOZmlsdGVyZWRNaW5lcnMIBQwkdDA5MjQ2MTAyMTICXzQBE2dldENoYWluTGFzdEJsb2NrSWQBB2NoYWluSWQICQEJY2hhaW5NZXRhAQUHY2hhaW5JZAJfMgANJHQwMTAyNzgxMDM4NQkBCWJsb2NrTWV0YQEFEm1haW5DaGFpbkxhc3RCbG9jawAMbWNsYklnbm9yZWQxCAUNJHQwMTAyNzgxMDM4NQJfMQAObWFpbkNoYWluRXBvY2gIBQ0kdDAxMDI3ODEwMzg1Al8yABNtYWluQ2hhaW5QYXJlbnRIYXNoCAUNJHQwMTAyNzgxMDM4NQJfMwASbWFpbkNoYWluR2VuZXJhdG9yCAUNJHQwMTAyNzgxMDM4NQJfNAEbY2FsY3VsYXRlRmluYWxpemVkQmxvY2tIYXNoAwhjdXJNaW5lcgxjdXJQcmV2RXBvY2gQY3VyTGFzdEJsb2NrSGFzaAQLb2Zmc2V0c18xMDAJALwJAgJkOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OgIABAtoYWxmQmFsYW5jZQkAaQIFFGNvbXB1dGVkVG90YWxCYWxhbmNlAAIKAQRzdGVwAgRwcmV2BG5leHQEDSR0MDEwNzUxMTA4MTUFBHByZXYECXRoaXNFcG9jaAgFDSR0MDEwNzUxMTA4MTUCXzEEDHRvdGFsQmFsYW5jZQgFDSR0MDEwNzUxMTA4MTUCXzIEDm1heWJlU2FmZUVwb2NoCAUNJHQwMTA3NTExMDgxNQJfMwQKcHJldk1pbmVycwgFDSR0MDEwNzUxMTA4MTUCXzQEByRtYXRjaDAFDm1heWJlU2FmZUVwb2NoAwkAAQIFByRtYXRjaDACBFVuaXQEDSR0MDEwODczMTEwMzkDCQAAAgUJdGhpc0Vwb2NoBQZoZWlnaHQJAJUKAwUIY3VyTWluZXIFDGN1clByZXZFcG9jaAUQY3VyTGFzdEJsb2NrSGFzaAkBBXZhbHVlAQkBCWVwb2NoTWV0YQEFCXRoaXNFcG9jaAQFbWluZXIIBQ0kdDAxMDg3MzExMDM5Al8xBAlwcmV2RXBvY2gIBQ0kdDAxMDg3MzExMDM5Al8yBA1sYXN0QmxvY2tIYXNoCAUNJHQwMTA4NzMxMTAzOQJfMwMJAAACBQlwcmV2RXBvY2gAAAkAlgoEBQl0aGlzRXBvY2gFDHRvdGFsQmFsYW5jZQUNbGFzdEJsb2NrSGFzaAUJYWxsTWluZXJzBA0kdDAxMTE2NDExMzY2AwkBD2NvbnRhaW5zRWxlbWVudAIFCnByZXZNaW5lcnMFBW1pbmVyCQCUCgIFDHRvdGFsQmFsYW5jZQUKcHJldk1pbmVycwkAlAoCCQBkAgUMdG90YWxCYWxhbmNlCQERZ2VuZXJhdGluZ0JhbGFuY2UBBQVtaW5lcgkAzAgCBQVtaW5lcgUKcHJldk1pbmVycwQPbmV3VG90YWxCYWxhbmNlCAUNJHQwMTExNjQxMTM2NgJfMQQJbmV3TWluZXJzCAUNJHQwMTExNjQxMTM2NgJfMgMJAGYCBQ9uZXdUb3RhbEJhbGFuY2UFC2hhbGZCYWxhbmNlCQCWCgQFCXRoaXNFcG9jaAUPbmV3VG90YWxCYWxhbmNlBQ1sYXN0QmxvY2tIYXNoBQlhbGxNaW5lcnMJAJYKBAUJcHJldkVwb2NoBQ9uZXdUb3RhbEJhbGFuY2UFBHVuaXQFCW5ld01pbmVycwUEcHJldgQNJHQwMTE1OTkxMTczMgoAAiRsCQDOCAIFC29mZnNldHNfMTAwBQtvZmZzZXRzXzEwMAoAAiRzCQCQAwEFAiRsCgAFJGFjYzAJAJYKBAUGaGVpZ2h0AAAFBHVuaXQFA25pbAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEEc3RlcAIFAiRhCQCRAwIFAiRsBQIkaQoBBSRmMF8yAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQACAQIVTGlzdCBzaXplIGV4Y2VlZHMgMjAwCQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgADAAQABQAGAAcACAAJAAoACwAMAA0ADgAPABAAEQASABMAFAAVABYAFwAYABkAGgAbABwAHQAeAB8AIAAhACIAIwAkACUAJgAnACgAKQAqACsALAAtAC4ALwAwADEAMgAzADQANQA2ADcAOAA5ADoAOwA8AD0APgA/AEAAQQBCAEMARABFAEYARwBIAEkASgBLAEwATQBOAE8AUABRAFIAUwBUAFUAVgBXAFgAWQBaAFsAXABdAF4AXwBgAGEAYgBjAGQAZQBmAGcAaABpAGoAawBsAG0AbgBvAHAAcQByAHMAdAB1AHYAdwB4AHkAegB7AHwAfQB+AH8AgAEAgQEAggEAgwEAhAEAhQEAhgEAhwEAiAEAiQEAigEAiwEAjAEAjQEAjgEAjwEAkAEAkQEAkgEAkwEAlAEAlQEAlgEAlwEAmAEAmQEAmgEAmwEAnAEAnQEAngEAnwEAoAEAoQEAogEAowEApAEApQEApgEApwEAqAEAqQEAqgEAqwEArAEArQEArgEArwEAsAEAsQEAsgEAswEAtAEAtQEAtgEAtwEAuAEAuQEAugEAuwEAvAEAvQEAvgEAvwEAwAEAwQEAwgEAwwEAxAEAxQEAxgEAxwEAyAEEDWZhbGxiYWNrRXBvY2gIBQ0kdDAxMTU5OTExNzMyAl8xBAx0b3RhbEJhbGFuY2UIBQ0kdDAxMTU5OTExNzMyAl8yBBVmaW5hbGl6ZWRCbG9ja0hhc2hPcHQIBQ0kdDAxMTU5OTExNzMyAl8zBAZtaW5lcnMIBQ0kdDAxMTU5OTExNzMyAl80BAckbWF0Y2gwBRVmaW5hbGl6ZWRCbG9ja0hhc2hPcHQDCQABAgUHJG1hdGNoMAIGU3RyaW5nBBJmaW5hbGl6ZWRCbG9ja0hhc2gFByRtYXRjaDAFEmZpbmFsaXplZEJsb2NrSGFzaAgJAQV2YWx1ZQEJAQllcG9jaE1ldGEBBQ1mYWxsYmFja0Vwb2NoAl8zARFzdXBwb3J0aW5nQmFsYW5jZQEHY2hhaW5JZAoBCmFkZEJhbGFuY2UCA2FjYwxnZW5lcmF0b3JTdHIEDSR0MDEyMDAyMTIwMzgFA2FjYwQMdG90YWxCYWxhbmNlCAUNJHQwMTIwMDIxMjAzOAJfMQQKZ2VuZXJhdG9ycwgFDSR0MDEyMDAyMTIwMzgCXzIECWdlbmVyYXRvcgkBEUBleHRyTmF0aXZlKDEwNjIpAQUMZ2VuZXJhdG9yU3RyAwkBD2NvbnRhaW5zRWxlbWVudAIFCmdlbmVyYXRvcnMFCWdlbmVyYXRvcgUDYWNjBAdiYWxhbmNlCQERZ2VuZXJhdGluZ0JhbGFuY2UBBQlnZW5lcmF0b3IJAJQKAgkAZAIFDHRvdGFsQmFsYW5jZQUHYmFsYW5jZQkAzQgCBQpnZW5lcmF0b3JzBQlnZW5lcmF0b3IEDWFsbEdlbmVyYXRvcnMJALwJAgkBEUBleHRyTmF0aXZlKDEwNTgpAQkBDXN1cHBvcnRlcnNLZXkBBQdjaGFpbklkBQNTRVAEDSR0MDEyMzYwMTI0MjUKAAIkbAUNYWxsR2VuZXJhdG9ycwoAAiRzCQCQAwEFAiRsCgAFJGFjYzAJAJQKAgAABQNuaWwKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBCmFkZEJhbGFuY2UCBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECFUxpc3Qgc2l6ZSBleGNlZWRzIDEwMAkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIAMwA0ADUANgA3ADgAOQA6ADsAPAA9AD4APwBAAEEAQgBDAEQARQBGAEcASABJAEoASwBMAE0ATgBPAFAAUQBSAFMAVABVAFYAVwBYAFkAWgBbAFwAXQBeAF8AYABhAGIAYwBkBAdiYWxhbmNlCAUNJHQwMTIzNjAxMjQyNQJfMQQCX2cIBQ0kdDAxMjM2MDEyNDI1Al8yBQdiYWxhbmNlAQ9pc0NvbnRyYWN0U2V0dXAACQEJaXNEZWZpbmVkAQkAnwgBBQ5taW5lclJld2FyZEtleQERZW5zdXJlTWluaW5nRXBvY2gBCWdlbmVyYXRvcgMJAQIhPQIJAKUIAQUJZ2VuZXJhdG9yBRFjb21wdXRlZEdlbmVyYXRvcgkAAgEJAKwCAgkArAICCQCsAgIJAKwCAgkA2AQBCAUJZ2VuZXJhdG9yBWJ5dGVzAhsgaXMgbm90IGFsbG93ZWQgdG8gbWluZSBpbiAJAKQDAQUGaGVpZ2h0AhEgZXBvY2guIEV4cGVjdGVkIAURY29tcHV0ZWRHZW5lcmF0b3IFBHVuaXQBEmlzUmVmZXJlbmNlQ29ycmVjdAIJcmVmZXJlbmNlCWxhc3RCbG9jawMJAAACBQlyZWZlcmVuY2UFCWxhc3RCbG9jawUEdW5pdAkAAgEJAKwCAgkArAICCQCsAgICMEV4cGVjdGVkIGEgcmVmZXJlbmNlIHRvIHRoZSBjaGFpbiBsYXN0IGJsb2NrOiAweAUJbGFzdEJsb2NrAgkuIEdvdDogMHgFCXJlZmVyZW5jZQEPY2hhaW5Jc0luYWN0aXZlAQdjaGFpbklkBAxmaXJzdEJsb2NrSWQJARFAZXh0ck5hdGl2ZSgxMDU4KQEJARRjaGFpbkZpcnN0QmxvY2tJZEtleQEFB2NoYWluSWQEFGZpcnN0VmFsaWRBbHRDaGFpbklkCQELdmFsdWVPckVsc2UCCQCfCAEFF2ZpcnN0VmFsaWRBbHRDaGFpbklkS2V5AAADCQBmAgUUZmlyc3RWYWxpZEFsdENoYWluSWQFB2NoYWluSWQGCQBmAggJAQlibG9ja01ldGEBCQERQGV4dHJOYXRpdmUoMTA1OCkBBRFmaW5hbGl6ZWRCbG9ja0tleQJfMQgJAQlibG9ja01ldGEBBQxmaXJzdEJsb2NrSWQCXzEBDG1pbmVyQ2hhaW5JZAEFbWluZXIJAQt2YWx1ZU9yRWxzZQIJAJ8IAQkBD21pbmVyQ2hhaW5JZEtleQEFBW1pbmVyCQCfCAEJAKwCAgIJY2hhaW5JZE9mCQClCAEFBW1pbmVyAR1lbnN1cmVFeHBlY3RlZE9ySW5hY3RpdmVDaGFpbgMJZ2VuZXJhdG9yD2V4cGVjdGVkQ2hhaW5JZBBjaGVja0hlaWdodEJsb2NrBA9oZWlnaHRJc0NvcnJlY3QEByRtYXRjaDAFEGNoZWNrSGVpZ2h0QmxvY2sDCQABAgUHJG1hdGNoMAIGU3RyaW5nBAlibG9ja0hhc2gFByRtYXRjaDAEFGxhc3RNaW5lZEJsb2NrSGVpZ2h0CQEMbGFzdEhlaWdodEJ5AgUJZ2VuZXJhdG9yBQttYWluQ2hhaW5JZAkAZgIJAGQCCAkBCWJsb2NrTWV0YQEFCWJsb2NrSGFzaAJfMQABBRRsYXN0TWluZWRCbG9ja0hlaWdodAYEByRtYXRjaDAJAQxtaW5lckNoYWluSWQBBQlnZW5lcmF0b3IDCQABAgUHJG1hdGNoMAIDSW50BAljdXJyZW50SWQFByRtYXRjaDADAwkAAAIFCWN1cnJlbnRJZAUPZXhwZWN0ZWRDaGFpbklkBgMJAQ9jaGFpbklzSW5hY3RpdmUBBQljdXJyZW50SWQFD2hlaWdodElzQ29ycmVjdAcFBHVuaXQJAAIBCQCsAgICHG1pbmVyIGlzIG1pbmluZyBvdGhlciBjaGFpbiAJAKQDAQUJY3VycmVudElkBQR1bml0ARJlbnN1cmVDb3JyZWN0RXBvY2gBBWVwb2NoAwkAAAIFBWVwb2NoBQZoZWlnaHQFBHVuaXQJAAIBCQCsAgIJAKwCAgkArAICAhpFeHBlY3RlZCBibG9jayBmcm9tIGVwb2NoIAkApAMBBQZoZWlnaHQCBi4gR290IAkApAMBBQVlcG9jaAEMYWRkU3VwcG9ydGVyAgdjaGFpbklkCWdlbmVyYXRvcgQNc3VwcG9ydGVyc1N0cgkBEUBleHRyTmF0aXZlKDEwNTgpAQkBDXN1cHBvcnRlcnNLZXkBBQdjaGFpbklkBApzdXBwb3J0ZXJzCQC8CQIFDXN1cHBvcnRlcnNTdHIFA1NFUAMJAQ9jb250YWluc0VsZW1lbnQCBQpzdXBwb3J0ZXJzCQClCAEFCWdlbmVyYXRvcgUDbmlsCQDMCAIJAQtTdHJpbmdFbnRyeQIJAQ1zdXBwb3J0ZXJzS2V5AQUHY2hhaW5JZAkArAICCQCsAgIFDXN1cHBvcnRlcnNTdHIFA1NFUAkApQgBBQlnZW5lcmF0b3IFA25pbAEJc2V0T3JGYWlsAgVmbGFncwVpbmRleAMJAGYCAAAFBWluZGV4CQACAQkArAICAiJDYW4ndCB3aXRoZHJhdyBhdCBuZWdhdGl2ZSBpbmRleDogCQCkAwEFBWluZGV4BAlmbGFnc1NpemUJALECAQUFZmxhZ3MDCQBnAgUFaW5kZXgFCWZsYWdzU2l6ZQQJYWRkWmVyb2VzCQBlAgUFaW5kZXgFCWZsYWdzU2l6ZQMJAGYCBQlhZGRaZXJvZXMJALECAQUJemVyb2VzU3RyCQACAQkArAICCQCsAgICCkNhbid0IGFkZCAJAKQDAQUJYWRkWmVyb2VzAiUgZW1wdHkgZmxhZ3MuIENvbnRhY3Qgd2l0aCBkZXZlbG9wZXJzCQCsAgIJAKwCAgUFZmxhZ3MJAK8CAgUJemVyb2VzU3RyBQlhZGRaZXJvZXMCATEEBHRhaWwJALACAgUFZmxhZ3MFBWluZGV4BAdhdEluZGV4CQCvAgIFBHRhaWwAAQMJAAACBQdhdEluZGV4AgEwCQCsAgIJAKwCAgkArwICBQVmbGFncwUFaW5kZXgCATEJALACAgUEdGFpbAABCQACAQkArAICCQCsAgICClRyYW5zZmVyICMJAKQDAQUFaW5kZXgCFyBoYXMgYmVlbiBhbHJlYWR5IHRha2VuARF2YWxpZGF0ZUJsb2NrSGFzaAEGaGV4U3RyBAxkZWNvZGVkQnl0ZXMJAN0EAQUGaGV4U3RyAwkBAiE9AgkAyAEBBQxkZWNvZGVkQnl0ZXMAIAkAAgECF2ludmFsaWQgYmxvY2sgaWQgbGVuZ3RoBQZoZXhTdHIBHWdldFVwZGF0ZUZpbmFsaXplZEJsb2NrQWN0aW9uAwZjYWxsZXIPbmV3QmxvY2tIYXNoSGV4CXByZXZFcG9jaAQXY3VyRmluYWxpemVkQmxvY2tIZWlnaHQICQEJYmxvY2tNZXRhAQkBEUBleHRyTmF0aXZlKDEwNTgpAQURZmluYWxpemVkQmxvY2tLZXkCXzEEFW5ld0ZpbmFsaXplZEJsb2NrSGFzaAkBG2NhbGN1bGF0ZUZpbmFsaXplZEJsb2NrSGFzaAMFBmNhbGxlcgUJcHJldkVwb2NoBQ9uZXdCbG9ja0hhc2hIZXgDAwkAAAIFFW5ld0ZpbmFsaXplZEJsb2NrSGFzaAUPbmV3QmxvY2tIYXNoSGV4BgkAZgIICQEJYmxvY2tNZXRhAQUVbmV3RmluYWxpemVkQmxvY2tIYXNoAl8xBRdjdXJGaW5hbGl6ZWRCbG9ja0hlaWdodAkAzAgCCQELU3RyaW5nRW50cnkCBRFmaW5hbGl6ZWRCbG9ja0tleQUVbmV3RmluYWxpemVkQmxvY2tIYXNoBQNuaWwFA25pbA0BaQEOYXBwZW5kQmxvY2tfdjIDDGJsb2NrSGFzaEhleAxyZWZlcmVuY2VIZXgaZWxUb0NsVHJhbnNmZXJzUm9vdEhhc2hIZXgEC2NoZWNrQ2xUb0VsCQEecmVxdWlyZUNsVG9FbFRyYW5zZmVyc0Rpc2FibGVkAAMJAAACBQtjaGVja0NsVG9FbAULY2hlY2tDbFRvRWwDCQECIT0CBQ50aGlzRXBvY2hNaW5lcggFAWkMb3JpZ2luQ2FsbGVyBAckbWF0Y2gwBQ50aGlzRXBvY2hNaW5lcgMJAAECBQckbWF0Y2gwAgdBZGRyZXNzBAplcG9jaE1pbmVyBQckbWF0Y2gwCQACAQkArAICAjlub3QgYWxsb3dlZCB0byBmb3JnZSBibG9ja3MgaW4gdGhpcyBlcG9jaCwgZXhwZWN0ZWQgZnJvbSAJAKUIAQUKZXBvY2hNaW5lcgkAAgECQG5vdCBhbGxvd2VkIHRvIGZvcmdlIGJsb2NrcyBpbiB0aGlzIGVwb2NoLCBlcG9jaCBtaW5lciBpcyBhYnNlbnQEB2NoYWluSWQJAQt2YWx1ZU9yRWxzZQIJAQxtaW5lckNoYWluSWQBCAUBaQxvcmlnaW5DYWxsZXIFC21haW5DaGFpbklkBA0kdDAxNjU4MjE2NjMzCQEJY2hhaW5NZXRhAQUHY2hhaW5JZAQLY2hhaW5IZWlnaHQIBQ0kdDAxNjU4MjE2NjMzAl8xBAtsYXN0QmxvY2tJZAgFDSR0MDE2NTgyMTY2MzMCXzIEDmNoZWNrUmVmZXJlbmNlCQESaXNSZWZlcmVuY2VDb3JyZWN0AgUMcmVmZXJlbmNlSGV4BQtsYXN0QmxvY2tJZAMJAAACBQ5jaGVja1JlZmVyZW5jZQUOY2hlY2tSZWZlcmVuY2UEDm5ld0NoYWluSGVpZ2h0CQBkAgULY2hhaW5IZWlnaHQAAQQOY2hlY2tCbG9ja0hhc2gJARF2YWxpZGF0ZUJsb2NrSGFzaAEFDGJsb2NrSGFzaEhleAMJAAACBQ5jaGVja0Jsb2NrSGFzaAUOY2hlY2tCbG9ja0hhc2gJAMwIAgkBEG1rQmxvY2tNZXRhRW50cnkHBQxibG9ja0hhc2hIZXgFDm5ld0NoYWluSGVpZ2h0BQtsYXN0QmxvY2tJZAgFAWkMb3JpZ2luQ2FsbGVyBQdjaGFpbklkBRplbFRvQ2xUcmFuc2ZlcnNSb290SGFzaEhleAD///////////8BCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQESY2hhaW5MYXN0SGVpZ2h0S2V5AgUHY2hhaW5JZAgFAWkMb3JpZ2luQ2FsbGVyBQ5uZXdDaGFpbkhlaWdodAkAzAgCCQEQbWtDaGFpbk1ldGFFbnRyeQMFB2NoYWluSWQFDm5ld0NoYWluSGVpZ2h0BQxibG9ja0hhc2hIZXgJAMwIAgkBC1N0cmluZ0VudHJ5AgkBDGVwb2NoTWV0YUtleQEFBmhlaWdodAkArAICCQCsAgIJAKwCAgkArAICCQClCAEJAQV2YWx1ZQEFDnRoaXNFcG9jaE1pbmVyBQNTRVAJAKQDAQUMdGhpc0Vwb2NoUmVmBQNTRVAFDGJsb2NrSGFzaEhleAUDbmlsCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBDmFwcGVuZEJsb2NrX3YzBAxibG9ja0hhc2hIZXgMcmVmZXJlbmNlSGV4GmVsVG9DbFRyYW5zZmVyc1Jvb3RIYXNoSGV4F2xhc3RDbFRvRWxUcmFuc2ZlckluZGV4BAtjaGVja0VsVG9DbAkBHXJlcXVpcmVDbFRvRWxUcmFuc2ZlcnNFbmFibGVkAAMJAAACBQtjaGVja0VsVG9DbAULY2hlY2tFbFRvQ2wEC2NoZWNrQ2FsbGVyAwkAAAIFDnRoaXNFcG9jaE1pbmVyCAUBaQxvcmlnaW5DYWxsZXIGBAckbWF0Y2gwBQ50aGlzRXBvY2hNaW5lcgMJAAECBQckbWF0Y2gwAgdBZGRyZXNzBAplcG9jaE1pbmVyBQckbWF0Y2gwCQACAQkArAICAjlub3QgYWxsb3dlZCB0byBmb3JnZSBibG9ja3MgaW4gdGhpcyBlcG9jaCwgZXhwZWN0ZWQgZnJvbSAJAKUIAQUKZXBvY2hNaW5lcgkAAgECQG5vdCBhbGxvd2VkIHRvIGZvcmdlIGJsb2NrcyBpbiB0aGlzIGVwb2NoLCBlcG9jaCBtaW5lciBpcyBhYnNlbnQDCQAAAgULY2hlY2tDYWxsZXIFC2NoZWNrQ2FsbGVyBAdjaGFpbklkCQELdmFsdWVPckVsc2UCCQEMbWluZXJDaGFpbklkAQgFAWkMb3JpZ2luQ2FsbGVyBQttYWluQ2hhaW5JZAQNJHQwMTc4NjAxNzkxMQkBCWNoYWluTWV0YQEFB2NoYWluSWQEC2NoYWluSGVpZ2h0CAUNJHQwMTc4NjAxNzkxMQJfMQQLbGFzdEJsb2NrSWQIBQ0kdDAxNzg2MDE3OTExAl8yBA5jaGVja1JlZmVyZW5jZQkBEmlzUmVmZXJlbmNlQ29ycmVjdAIFDHJlZmVyZW5jZUhleAULbGFzdEJsb2NrSWQDCQAAAgUOY2hlY2tSZWZlcmVuY2UFDmNoZWNrUmVmZXJlbmNlBA5jaGVja1RyYW5zZmVycwkBFmVuc3VyZUNvcnJlY3RUcmFuc2ZlcnMDCAkBCWJsb2NrTWV0YQEFDHJlZmVyZW5jZUhleAJfNwUXbGFzdENsVG9FbFRyYW5zZmVySW5kZXgHAwkAAAIFDmNoZWNrVHJhbnNmZXJzBQ5jaGVja1RyYW5zZmVycwQObmV3Q2hhaW5IZWlnaHQJAGQCBQtjaGFpbkhlaWdodAABBA5jaGVja0Jsb2NrSGFzaAkBEXZhbGlkYXRlQmxvY2tIYXNoAQUMYmxvY2tIYXNoSGV4AwkAAAIFDmNoZWNrQmxvY2tIYXNoBQ5jaGVja0Jsb2NrSGFzaAkAzAgCCQEQbWtCbG9ja01ldGFFbnRyeQcFDGJsb2NrSGFzaEhleAUObmV3Q2hhaW5IZWlnaHQFC2xhc3RCbG9ja0lkCAUBaQxvcmlnaW5DYWxsZXIFB2NoYWluSWQFGmVsVG9DbFRyYW5zZmVyc1Jvb3RIYXNoSGV4BRdsYXN0Q2xUb0VsVHJhbnNmZXJJbmRleAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBEmNoYWluTGFzdEhlaWdodEtleQIFB2NoYWluSWQIBQFpDG9yaWdpbkNhbGxlcgUObmV3Q2hhaW5IZWlnaHQJAMwIAgkBEG1rQ2hhaW5NZXRhRW50cnkDBQdjaGFpbklkBQ5uZXdDaGFpbkhlaWdodAUMYmxvY2tIYXNoSGV4CQDMCAIJAQtTdHJpbmdFbnRyeQIJAQxlcG9jaE1ldGFLZXkBBQZoZWlnaHQJAKwCAgkArAICCQCsAgIJAKwCAgkApQgBCQEFdmFsdWUBBQ50aGlzRXBvY2hNaW5lcgUDU0VQCQCkAwEFDHRoaXNFcG9jaFJlZgUDU0VQBQxibG9ja0hhc2hIZXgFA25pbAkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgFpARJleHRlbmRNYWluQ2hhaW5fdjIEDGJsb2NrSGFzaEhleAxyZWZlcmVuY2VIZXgFZXBvY2gaZWxUb0NsVHJhbnNmZXJzUm9vdEhhc2hIZXgEC2NoZWNrQ2xUb0VsCQEecmVxdWlyZUNsVG9FbFRyYW5zZmVyc0Rpc2FibGVkAAMJAAACBQtjaGVja0NsVG9FbAULY2hlY2tDbFRvRWwEDmNoZWNrQmxvY2tIYXNoCQERdmFsaWRhdGVCbG9ja0hhc2gBBQxibG9ja0hhc2hIZXgDCQAAAgUOY2hlY2tCbG9ja0hhc2gFDmNoZWNrQmxvY2tIYXNoBApjaGVja0Vwb2NoCQESZW5zdXJlQ29ycmVjdEVwb2NoAQUFZXBvY2gDCQAAAgUKY2hlY2tFcG9jaAUKY2hlY2tFcG9jaAQOY2hlY2tHZW5lcmF0b3IJARFlbnN1cmVNaW5pbmdFcG9jaAEIBQFpDG9yaWdpbkNhbGxlcgMJAAACBQ5jaGVja0dlbmVyYXRvcgUOY2hlY2tHZW5lcmF0b3IECmNoZWNrQ2hhaW4JAR1lbnN1cmVFeHBlY3RlZE9ySW5hY3RpdmVDaGFpbgMIBQFpDG9yaWdpbkNhbGxlcgULbWFpbkNoYWluSWQFBHVuaXQDCQAAAgUKY2hlY2tDaGFpbgUKY2hlY2tDaGFpbgQOY2hlY2tSZWZlcmVuY2UJARJpc1JlZmVyZW5jZUNvcnJlY3QCBQxyZWZlcmVuY2VIZXgFEm1haW5DaGFpbkxhc3RCbG9jawMJAAACBQ5jaGVja1JlZmVyZW5jZQUOY2hlY2tSZWZlcmVuY2UEDXRoaXNFcG9jaE1ldGEEByRtYXRjaDAJAQllcG9jaE1ldGEBBQZoZWlnaHQDCQABAgUHJG1hdGNoMAIEVW5pdAkBC1N0cmluZ0VudHJ5AgkBDGVwb2NoTWV0YUtleQEFBmhlaWdodAkArAICCQCsAgIJAKwCAgkArAICCQClCAEIBQFpDG9yaWdpbkNhbGxlcgUDU0VQCQCkAwEFDm1haW5DaGFpbkVwb2NoBQNTRVAFDGJsb2NrSGFzaEhleAQFb3RoZXIFByRtYXRjaDAJAAIBAhVFcG9jaCBhbHJlYWR5IHN0YXJ0ZWQEFHVwZGF0ZUZpbmFsaXplZEJsb2NrCQEdZ2V0VXBkYXRlRmluYWxpemVkQmxvY2tBY3Rpb24DCAUBaQxvcmlnaW5DYWxsZXIFDGJsb2NrSGFzaEhleAUObWFpbkNoYWluRXBvY2gEDm5ld0NoYWluSGVpZ2h0CQBkAgUPbWFpbkNoYWluSGVpZ2h0AAEJAM4IAgkAzAgCCQEQbWtCbG9ja01ldGFFbnRyeQcFDGJsb2NrSGFzaEhleAUObmV3Q2hhaW5IZWlnaHQFEm1haW5DaGFpbkxhc3RCbG9jawgFAWkMb3JpZ2luQ2FsbGVyBQttYWluQ2hhaW5JZAUaZWxUb0NsVHJhbnNmZXJzUm9vdEhhc2hIZXgA////////////AQkAzAgCCQEQbWtDaGFpbk1ldGFFbnRyeQMFC21haW5DaGFpbklkBQ5uZXdDaGFpbkhlaWdodAUMYmxvY2tIYXNoSGV4CQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEPbWluZXJDaGFpbklkS2V5AQgFAWkMb3JpZ2luQ2FsbGVyBQttYWluQ2hhaW5JZAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBEmNoYWluTGFzdEhlaWdodEtleQIFC21haW5DaGFpbklkCAUBaQxvcmlnaW5DYWxsZXIFDm5ld0NoYWluSGVpZ2h0CQDMCAIFDXRoaXNFcG9jaE1ldGEFA25pbAUUdXBkYXRlRmluYWxpemVkQmxvY2sJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4BaQESZXh0ZW5kTWFpbkNoYWluX3YzBQxibG9ja0hhc2hIZXgMcmVmZXJlbmNlSGV4BWVwb2NoGmVsVG9DbFRyYW5zZmVyc1Jvb3RIYXNoSGV4F2xhc3RDbFRvRWxUcmFuc2ZlckluZGV4BAtjaGVja0VsVG9DbAkBHXJlcXVpcmVDbFRvRWxUcmFuc2ZlcnNFbmFibGVkAAMJAAACBQtjaGVja0VsVG9DbAULY2hlY2tFbFRvQ2wEDmNoZWNrQmxvY2tIYXNoCQERdmFsaWRhdGVCbG9ja0hhc2gBBQxibG9ja0hhc2hIZXgDCQAAAgUOY2hlY2tCbG9ja0hhc2gFDmNoZWNrQmxvY2tIYXNoBApjaGVja0Vwb2NoCQESZW5zdXJlQ29ycmVjdEVwb2NoAQUFZXBvY2gDCQAAAgUKY2hlY2tFcG9jaAUKY2hlY2tFcG9jaAQOY2hlY2tHZW5lcmF0b3IJARFlbnN1cmVNaW5pbmdFcG9jaAEIBQFpDG9yaWdpbkNhbGxlcgMJAAACBQ5jaGVja0dlbmVyYXRvcgUOY2hlY2tHZW5lcmF0b3IECmNoZWNrQ2hhaW4JAR1lbnN1cmVFeHBlY3RlZE9ySW5hY3RpdmVDaGFpbgMIBQFpDG9yaWdpbkNhbGxlcgULbWFpbkNoYWluSWQFBHVuaXQDCQAAAgUKY2hlY2tDaGFpbgUKY2hlY2tDaGFpbgQOY2hlY2tSZWZlcmVuY2UJARJpc1JlZmVyZW5jZUNvcnJlY3QCBQxyZWZlcmVuY2VIZXgFEm1haW5DaGFpbkxhc3RCbG9jawMJAAACBQ5jaGVja1JlZmVyZW5jZQUOY2hlY2tSZWZlcmVuY2UEDmNoZWNrVHJhbnNmZXJzCQEWZW5zdXJlQ29ycmVjdFRyYW5zZmVycwMICQEJYmxvY2tNZXRhAQUMcmVmZXJlbmNlSGV4Al83BRdsYXN0Q2xUb0VsVHJhbnNmZXJJbmRleAYDCQAAAgUOY2hlY2tUcmFuc2ZlcnMFDmNoZWNrVHJhbnNmZXJzBA10aGlzRXBvY2hNZXRhBAckbWF0Y2gwCQEJZXBvY2hNZXRhAQUGaGVpZ2h0AwkAAQIFByRtYXRjaDACBFVuaXQJAQtTdHJpbmdFbnRyeQIJAQxlcG9jaE1ldGFLZXkBBQZoZWlnaHQJAKwCAgkArAICCQCsAgIJAKwCAgkApQgBCAUBaQxvcmlnaW5DYWxsZXIFA1NFUAkApAMBBQ5tYWluQ2hhaW5FcG9jaAUDU0VQBQxibG9ja0hhc2hIZXgEBW90aGVyBQckbWF0Y2gwCQACAQIVRXBvY2ggYWxyZWFkeSBzdGFydGVkAwkAAAIFDXRoaXNFcG9jaE1ldGEFDXRoaXNFcG9jaE1ldGEEFHVwZGF0ZUZpbmFsaXplZEJsb2NrCQEdZ2V0VXBkYXRlRmluYWxpemVkQmxvY2tBY3Rpb24DCAUBaQxvcmlnaW5DYWxsZXIFDGJsb2NrSGFzaEhleAUObWFpbkNoYWluRXBvY2gEDm5ld0NoYWluSGVpZ2h0CQBkAgUPbWFpbkNoYWluSGVpZ2h0AAEJAM4IAgkAzAgCCQEQbWtCbG9ja01ldGFFbnRyeQcFDGJsb2NrSGFzaEhleAUObmV3Q2hhaW5IZWlnaHQFEm1haW5DaGFpbkxhc3RCbG9jawgFAWkMb3JpZ2luQ2FsbGVyBQttYWluQ2hhaW5JZAUaZWxUb0NsVHJhbnNmZXJzUm9vdEhhc2hIZXgFF2xhc3RDbFRvRWxUcmFuc2ZlckluZGV4CQDMCAIJARBta0NoYWluTWV0YUVudHJ5AwULbWFpbkNoYWluSWQFDm5ld0NoYWluSGVpZ2h0BQxibG9ja0hhc2hIZXgJAMwIAgkBDEludGVnZXJFbnRyeQIJAQ9taW5lckNoYWluSWRLZXkBCAUBaQxvcmlnaW5DYWxsZXIFC21haW5DaGFpbklkCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQESY2hhaW5MYXN0SGVpZ2h0S2V5AgULbWFpbkNoYWluSWQIBQFpDG9yaWdpbkNhbGxlcgUObmV3Q2hhaW5IZWlnaHQJAMwIAgUNdGhpc0Vwb2NoTWV0YQUDbmlsBRR1cGRhdGVGaW5hbGl6ZWRCbG9jawkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgFpARBzdGFydEFsdENoYWluX3YyBAxibG9ja0hhc2hIZXgMcmVmZXJlbmNlSGV4BWVwb2NoGmVsVG9DbFRyYW5zZmVyc1Jvb3RIYXNoSGV4BAtjaGVja0NsVG9FbAkBHnJlcXVpcmVDbFRvRWxUcmFuc2ZlcnNEaXNhYmxlZAADCQAAAgULY2hlY2tDbFRvRWwFC2NoZWNrQ2xUb0VsBA5jaGVja0Jsb2NrSGFzaAkBEXZhbGlkYXRlQmxvY2tIYXNoAQUMYmxvY2tIYXNoSGV4AwkAAAIFDmNoZWNrQmxvY2tIYXNoBQ5jaGVja0Jsb2NrSGFzaAQKY2hlY2tFcG9jaAkBEmVuc3VyZUNvcnJlY3RFcG9jaAEFBWVwb2NoAwkAAAIFCmNoZWNrRXBvY2gFCmNoZWNrRXBvY2gEDmNoZWNrR2VuZXJhdG9yCQERZW5zdXJlTWluaW5nRXBvY2gBCAUBaQxvcmlnaW5DYWxsZXIDCQAAAgUOY2hlY2tHZW5lcmF0b3IFDmNoZWNrR2VuZXJhdG9yBA0kdDAyMTgwNTIxODgzCQEJYmxvY2tNZXRhAQUMcmVmZXJlbmNlSGV4BA5yZWZDaGFpbkhlaWdodAgFDSR0MDIxODA1MjE4ODMCXzEECHJlZkVwb2NoCAUNJHQwMjE4MDUyMTg4MwJfMgQGcmVmUmVmCAUNJHQwMjE4MDUyMTg4MwJfMwQMcmVmR2VuZXJhdG9yCAUNJHQwMjE4MDUyMTg4MwJfNAQOZmluYWxpemVkRXBvY2gICQEJYmxvY2tNZXRhAQkBEUBleHRyTmF0aXZlKDEwNTgpAQURZmluYWxpemVkQmxvY2tLZXkCXzIECGVwb2NoUmVmAwkAZwIFCHJlZkVwb2NoBQ5maW5hbGl6ZWRFcG9jaAUIcmVmRXBvY2gJAAIBCQCsAgIJAKwCAgkArAICCQCsAgICI2NhbiBub3Qgc3RhcnQgYWx0IGNoYWluIGZyb20gZXBvY2ggCQCkAwEFCHJlZkVwb2NoAggsIGVwb2NoIAkApAMBBQ5maW5hbGl6ZWRFcG9jaAINIGlzIGZpbmFsaXplZAQKY2hlY2tDaGFpbgkBHWVuc3VyZUV4cGVjdGVkT3JJbmFjdGl2ZUNoYWluAwgFAWkMb3JpZ2luQ2FsbGVyBQttYWluQ2hhaW5JZAUMcmVmZXJlbmNlSGV4AwkAAAIFCmNoZWNrQ2hhaW4FCmNoZWNrQ2hhaW4ECm5ld0NoYWluSWQJAGQCCQELdmFsdWVPckVsc2UCCQCfCAEFDmxhc3RDaGFpbklkS2V5AAAAAQQObmV3Q2hhaW5IZWlnaHQJAGQCCAkBCWJsb2NrTWV0YQEFDHJlZmVyZW5jZUhleAJfMQABBA10aGlzRXBvY2hNZXRhBAckbWF0Y2gwCQEJZXBvY2hNZXRhAQUGaGVpZ2h0AwkAAQIFByRtYXRjaDACBFVuaXQJAQtTdHJpbmdFbnRyeQIJAQxlcG9jaE1ldGFLZXkBBQZoZWlnaHQJAKwCAgkArAICCQCsAgIJAKwCAgkApQgBCAUBaQxvcmlnaW5DYWxsZXIFA1NFUAkApAMBBQhlcG9jaFJlZgUDU0VQBQxibG9ja0hhc2hIZXgEBW90aGVyBQckbWF0Y2gwCQACAQIVRXBvY2ggYWxyZWFkeSBzdGFydGVkCQDMCAIFDXRoaXNFcG9jaE1ldGEJAMwIAgkBEG1rQmxvY2tNZXRhRW50cnkHBQxibG9ja0hhc2hIZXgFDm5ld0NoYWluSGVpZ2h0BQxyZWZlcmVuY2VIZXgIBQFpDG9yaWdpbkNhbGxlcgUKbmV3Q2hhaW5JZAUaZWxUb0NsVHJhbnNmZXJzUm9vdEhhc2hIZXgA////////////AQkAzAgCCQELU3RyaW5nRW50cnkCCQEUY2hhaW5GaXJzdEJsb2NrSWRLZXkBBQpuZXdDaGFpbklkBQxibG9ja0hhc2hIZXgJAMwIAgkBEG1rQ2hhaW5NZXRhRW50cnkDBQpuZXdDaGFpbklkBQ5uZXdDaGFpbkhlaWdodAUMYmxvY2tIYXNoSGV4CQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEPbWluZXJDaGFpbklkS2V5AQgFAWkMb3JpZ2luQ2FsbGVyBQpuZXdDaGFpbklkCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQESY2hhaW5MYXN0SGVpZ2h0S2V5AgUKbmV3Q2hhaW5JZAgFAWkMb3JpZ2luQ2FsbGVyBQ5uZXdDaGFpbkhlaWdodAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBEmNoYWluTGFzdEhlaWdodEtleQIFC21haW5DaGFpbklkCAUBaQxvcmlnaW5DYWxsZXIFDm5ld0NoYWluSGVpZ2h0CQDMCAIJAQtTdHJpbmdFbnRyeQIJAQ1zdXBwb3J0ZXJzS2V5AQUKbmV3Q2hhaW5JZAkApQgBCAUBaQxvcmlnaW5DYWxsZXIJAMwIAgkBDEludGVnZXJFbnRyeQIFDmxhc3RDaGFpbklkS2V5BQpuZXdDaGFpbklkBQNuaWwJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4BaQEQc3RhcnRBbHRDaGFpbl92MwUMYmxvY2tIYXNoSGV4DHJlZmVyZW5jZUhleAVlcG9jaBplbFRvQ2xUcmFuc2ZlcnNSb290SGFzaEhleBdsYXN0Q2xUb0VsVHJhbnNmZXJJbmRleAQLY2hlY2tFbFRvQ2wJAR1yZXF1aXJlQ2xUb0VsVHJhbnNmZXJzRW5hYmxlZAADCQAAAgULY2hlY2tFbFRvQ2wFC2NoZWNrRWxUb0NsBA5jaGVja0Jsb2NrSGFzaAkBEXZhbGlkYXRlQmxvY2tIYXNoAQUMYmxvY2tIYXNoSGV4AwkAAAIFDmNoZWNrQmxvY2tIYXNoBQ5jaGVja0Jsb2NrSGFzaAQKY2hlY2tFcG9jaAkBEmVuc3VyZUNvcnJlY3RFcG9jaAEFBWVwb2NoAwkAAAIFCmNoZWNrRXBvY2gFCmNoZWNrRXBvY2gEDmNoZWNrR2VuZXJhdG9yCQERZW5zdXJlTWluaW5nRXBvY2gBCAUBaQxvcmlnaW5DYWxsZXIDCQAAAgUOY2hlY2tHZW5lcmF0b3IFDmNoZWNrR2VuZXJhdG9yBA0kdDAyMzY0MTIzNzYzCQEJYmxvY2tNZXRhAQUMcmVmZXJlbmNlSGV4BA5yZWZDaGFpbkhlaWdodAgFDSR0MDIzNjQxMjM3NjMCXzEECHJlZkVwb2NoCAUNJHQwMjM2NDEyMzc2MwJfMgQGcmVmUmVmCAUNJHQwMjM2NDEyMzc2MwJfMwQMcmVmR2VuZXJhdG9yCAUNJHQwMjM2NDEyMzc2MwJfNAQLcmVmSWdub3JlZDUIBQ0kdDAyMzY0MTIzNzYzAl81BAtyZWZJZ25vcmVkNggFDSR0MDIzNjQxMjM3NjMCXzYEEHJlZlRyYW5zZmVySW5kZXgIBQ0kdDAyMzY0MTIzNzYzAl83BA5maW5hbGl6ZWRFcG9jaAgJAQlibG9ja01ldGEBCQERQGV4dHJOYXRpdmUoMTA1OCkBBRFmaW5hbGl6ZWRCbG9ja0tleQJfMgQIZXBvY2hSZWYDCQBnAgUIcmVmRXBvY2gFDmZpbmFsaXplZEVwb2NoBQhyZWZFcG9jaAkAAgEJAKwCAgkArAICCQCsAgIJAKwCAgIjQ2FuIG5vdCBzdGFydCBhbHQgY2hhaW4gZnJvbSBlcG9jaCAJAKQDAQUIcmVmRXBvY2gCCCwgZXBvY2ggCQCkAwEFDmZpbmFsaXplZEVwb2NoAg0gaXMgZmluYWxpemVkBApjaGVja0NoYWluCQEdZW5zdXJlRXhwZWN0ZWRPckluYWN0aXZlQ2hhaW4DCAUBaQxvcmlnaW5DYWxsZXIFC21haW5DaGFpbklkBQxyZWZlcmVuY2VIZXgDCQAAAgUKY2hlY2tDaGFpbgUKY2hlY2tDaGFpbgQOY2hlY2tUcmFuc2ZlcnMJARZlbnN1cmVDb3JyZWN0VHJhbnNmZXJzAwUQcmVmVHJhbnNmZXJJbmRleAUXbGFzdENsVG9FbFRyYW5zZmVySW5kZXgGAwkAAAIFDmNoZWNrVHJhbnNmZXJzBQ5jaGVja1RyYW5zZmVycwQKbmV3Q2hhaW5JZAkAZAIJAQt2YWx1ZU9yRWxzZQIJAJ8IAQUObGFzdENoYWluSWRLZXkAAAABBA5uZXdDaGFpbkhlaWdodAkAZAIFDnJlZkNoYWluSGVpZ2h0AAEEDXRoaXNFcG9jaE1ldGEEByRtYXRjaDAJAQllcG9jaE1ldGEBBQZoZWlnaHQDCQABAgUHJG1hdGNoMAIEVW5pdAkBC1N0cmluZ0VudHJ5AgkBDGVwb2NoTWV0YUtleQEFBmhlaWdodAkArAICCQCsAgIJAKwCAgkArAICCQClCAEIBQFpDG9yaWdpbkNhbGxlcgUDU0VQCQCkAwEFCGVwb2NoUmVmBQNTRVAFDGJsb2NrSGFzaEhleAQFb3RoZXIFByRtYXRjaDAJAAIBAhVFcG9jaCBhbHJlYWR5IHN0YXJ0ZWQJAMwIAgUNdGhpc0Vwb2NoTWV0YQkAzAgCCQEQbWtCbG9ja01ldGFFbnRyeQcFDGJsb2NrSGFzaEhleAUObmV3Q2hhaW5IZWlnaHQFDHJlZmVyZW5jZUhleAgFAWkMb3JpZ2luQ2FsbGVyBQpuZXdDaGFpbklkBRplbFRvQ2xUcmFuc2ZlcnNSb290SGFzaEhleAUXbGFzdENsVG9FbFRyYW5zZmVySW5kZXgJAMwIAgkBC1N0cmluZ0VudHJ5AgkBFGNoYWluRmlyc3RCbG9ja0lkS2V5AQUKbmV3Q2hhaW5JZAUMYmxvY2tIYXNoSGV4CQDMCAIJARBta0NoYWluTWV0YUVudHJ5AwUKbmV3Q2hhaW5JZAUObmV3Q2hhaW5IZWlnaHQFDGJsb2NrSGFzaEhleAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBD21pbmVyQ2hhaW5JZEtleQEIBQFpDG9yaWdpbkNhbGxlcgUKbmV3Q2hhaW5JZAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBEmNoYWluTGFzdEhlaWdodEtleQIFCm5ld0NoYWluSWQIBQFpDG9yaWdpbkNhbGxlcgUObmV3Q2hhaW5IZWlnaHQJAMwIAgkBDEludGVnZXJFbnRyeQIJARJjaGFpbkxhc3RIZWlnaHRLZXkCBQttYWluQ2hhaW5JZAgFAWkMb3JpZ2luQ2FsbGVyBQ5uZXdDaGFpbkhlaWdodAkAzAgCCQELU3RyaW5nRW50cnkCCQENc3VwcG9ydGVyc0tleQEFCm5ld0NoYWluSWQJAKUIAQgFAWkMb3JpZ2luQ2FsbGVyCQDMCAIJAQxJbnRlZ2VyRW50cnkCBQ5sYXN0Q2hhaW5JZEtleQUKbmV3Q2hhaW5JZAUDbmlsCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBEWV4dGVuZEFsdENoYWluX3YyBQdjaGFpbklkDGJsb2NrSGFzaEhleAxyZWZlcmVuY2VIZXgFZXBvY2gaZWxUb0NsVHJhbnNmZXJzUm9vdEhhc2hIZXgEC2NoZWNrQ2xUb0VsCQEecmVxdWlyZUNsVG9FbFRyYW5zZmVyc0Rpc2FibGVkAAMJAAACBQtjaGVja0NsVG9FbAULY2hlY2tDbFRvRWwEDmNoZWNrQmxvY2tIYXNoCQERdmFsaWRhdGVCbG9ja0hhc2gBBQxibG9ja0hhc2hIZXgDCQAAAgUOY2hlY2tCbG9ja0hhc2gFDmNoZWNrQmxvY2tIYXNoBApjaGVja0Vwb2NoCQESZW5zdXJlQ29ycmVjdEVwb2NoAQUFZXBvY2gDCQAAAgUKY2hlY2tFcG9jaAUKY2hlY2tFcG9jaAQOY2hlY2tHZW5lcmF0b3IJARFlbnN1cmVNaW5pbmdFcG9jaAEIBQFpDG9yaWdpbkNhbGxlcgMJAAACBQ5jaGVja0dlbmVyYXRvcgUOY2hlY2tHZW5lcmF0b3IEE2NoYWluRmlyc3RCbG9ja01ldGEJAQlibG9ja01ldGEBCQERQGV4dHJOYXRpdmUoMTA1OCkBCQEUY2hhaW5GaXJzdEJsb2NrSWRLZXkBBQdjaGFpbklkBApjaGVja0NoYWluCQEdZW5zdXJlRXhwZWN0ZWRPckluYWN0aXZlQ2hhaW4DCAUBaQxvcmlnaW5DYWxsZXIFB2NoYWluSWQJANwEAQgFE2NoYWluRmlyc3RCbG9ja01ldGECXzMDCQAAAgUKY2hlY2tDaGFpbgUKY2hlY2tDaGFpbgQNJHQwMjYxMzcyNjE5MQkBCWNoYWluTWV0YQEFB2NoYWluSWQEC2NoYWluSGVpZ2h0CAUNJHQwMjYxMzcyNjE5MQJfMQQOY2hhaW5MYXN0QmxvY2sIBQ0kdDAyNjEzNzI2MTkxAl8yBA5jaGVja1JlZmVyZW5jZQkBEmlzUmVmZXJlbmNlQ29ycmVjdAIFDHJlZmVyZW5jZUhleAUOY2hhaW5MYXN0QmxvY2sDCQAAAgUOY2hlY2tSZWZlcmVuY2UFDmNoZWNrUmVmZXJlbmNlBA5uZXdDaGFpbkhlaWdodAkAZAIFC2NoYWluSGVpZ2h0AAEECXByZXZFcG9jaAgJAQlibG9ja01ldGEBBQxyZWZlcmVuY2VIZXgCXzIEE3VwZGF0ZU1haW5DaGFpbkRhdGEDCQBmAgkBEXN1cHBvcnRpbmdCYWxhbmNlAQUHY2hhaW5JZAkAaQIFFGNvbXB1dGVkVG90YWxCYWxhbmNlAAIEC2xhc3RDaGFpbklkCQELdmFsdWVPckVsc2UCCQCfCAEFDmxhc3RDaGFpbklkS2V5AAAEFHVwZGF0ZUZpbmFsaXplZEJsb2NrCQEdZ2V0VXBkYXRlRmluYWxpemVkQmxvY2tBY3Rpb24DCAUBaQxvcmlnaW5DYWxsZXIFDGJsb2NrSGFzaEhleAUJcHJldkVwb2NoCQDOCAIJAMwIAgkBDEludGVnZXJFbnRyeQIJARRjaGFpbkZvcmtlZEhlaWdodEtleQEFC21haW5DaGFpbklkCAUTY2hhaW5GaXJzdEJsb2NrTWV0YQJfMQkAzAgCCQEMSW50ZWdlckVudHJ5AgUObWFpbkNoYWluSWRLZXkFB2NoYWluSWQJAMwIAgkBDEludGVnZXJFbnRyeQIFF2ZpcnN0VmFsaWRBbHRDaGFpbklkS2V5CQBkAgULbGFzdENoYWluSWQAAQUDbmlsBRR1cGRhdGVGaW5hbGl6ZWRCbG9jawUDbmlsBA10aGlzRXBvY2hNZXRhBAckbWF0Y2gwCQEJZXBvY2hNZXRhAQUGaGVpZ2h0AwkAAQIFByRtYXRjaDACBFVuaXQJAQtTdHJpbmdFbnRyeQIJAQxlcG9jaE1ldGFLZXkBBQZoZWlnaHQJAKwCAgkArAICCQCsAgIJAKwCAgkApQgBCAUBaQxvcmlnaW5DYWxsZXIFA1NFUAkApAMBBQlwcmV2RXBvY2gFA1NFUAUMYmxvY2tIYXNoSGV4BAVvdGhlcgUHJG1hdGNoMAkAAgECFUVwb2NoIGFscmVhZHkgc3RhcnRlZAQddXBkYXRlTWFpbkNoYWluTGFzdE1pbmVkQmxvY2sDAwkAAAIFE3VwZGF0ZU1haW5DaGFpbkRhdGEFA25pbAkBAiE9AgkBC3ZhbHVlT3JFbHNlAgkBDG1pbmVyQ2hhaW5JZAEIBQFpDG9yaWdpbkNhbGxlcgAABQdjaGFpbklkBwkAzAgCCQEMSW50ZWdlckVudHJ5AgkBEmNoYWluTGFzdEhlaWdodEtleQIFC21haW5DaGFpbklkCAUBaQxvcmlnaW5DYWxsZXIIBRNjaGFpbkZpcnN0QmxvY2tNZXRhAl8xBQNuaWwFA25pbAkAzggCCQDOCAIJAM4IAgkAzAgCCQEQbWtCbG9ja01ldGFFbnRyeQcFDGJsb2NrSGFzaEhleAUObmV3Q2hhaW5IZWlnaHQFDHJlZmVyZW5jZUhleAgFAWkMb3JpZ2luQ2FsbGVyBQdjaGFpbklkBRplbFRvQ2xUcmFuc2ZlcnNSb290SGFzaEhleAD///////////8BCQDMCAIJARBta0NoYWluTWV0YUVudHJ5AwUHY2hhaW5JZAUObmV3Q2hhaW5IZWlnaHQFDGJsb2NrSGFzaEhleAkAzAgCBQ10aGlzRXBvY2hNZXRhCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEPbWluZXJDaGFpbklkS2V5AQgFAWkMb3JpZ2luQ2FsbGVyBQdjaGFpbklkCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQESY2hhaW5MYXN0SGVpZ2h0S2V5AgUHY2hhaW5JZAgFAWkMb3JpZ2luQ2FsbGVyBQ5uZXdDaGFpbkhlaWdodAUDbmlsBRN1cGRhdGVNYWluQ2hhaW5EYXRhCQEMYWRkU3VwcG9ydGVyAgUHY2hhaW5JZAgFAWkMb3JpZ2luQ2FsbGVyBR11cGRhdGVNYWluQ2hhaW5MYXN0TWluZWRCbG9jawkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgFpARFleHRlbmRBbHRDaGFpbl92MwYHY2hhaW5JZAxibG9ja0hhc2hIZXgMcmVmZXJlbmNlSGV4BWVwb2NoGmVsVG9DbFRyYW5zZmVyc1Jvb3RIYXNoSGV4F2xhc3RDbFRvRWxUcmFuc2ZlckluZGV4BAtjaGVja0VsVG9DbAkBHXJlcXVpcmVDbFRvRWxUcmFuc2ZlcnNFbmFibGVkAAMJAAACBQtjaGVja0VsVG9DbAULY2hlY2tFbFRvQ2wEDmNoZWNrQmxvY2tIYXNoCQERdmFsaWRhdGVCbG9ja0hhc2gBBQxibG9ja0hhc2hIZXgDCQAAAgUOY2hlY2tCbG9ja0hhc2gFDmNoZWNrQmxvY2tIYXNoBApjaGVja0Vwb2NoCQESZW5zdXJlQ29ycmVjdEVwb2NoAQUFZXBvY2gDCQAAAgUKY2hlY2tFcG9jaAUKY2hlY2tFcG9jaAQOY2hlY2tHZW5lcmF0b3IJARFlbnN1cmVNaW5pbmdFcG9jaAEIBQFpDG9yaWdpbkNhbGxlcgMJAAACBQ5jaGVja0dlbmVyYXRvcgUOY2hlY2tHZW5lcmF0b3IEE2NoYWluRmlyc3RCbG9ja01ldGEJAQlibG9ja01ldGEBCQERQGV4dHJOYXRpdmUoMTA1OCkBCQEUY2hhaW5GaXJzdEJsb2NrSWRLZXkBBQdjaGFpbklkBApjaGVja0NoYWluCQEdZW5zdXJlRXhwZWN0ZWRPckluYWN0aXZlQ2hhaW4DCAUBaQxvcmlnaW5DYWxsZXIFB2NoYWluSWQJANwEAQgFE2NoYWluRmlyc3RCbG9ja01ldGECXzMDCQAAAgUKY2hlY2tDaGFpbgUKY2hlY2tDaGFpbgQNJHQwMjg3MzUyODc4OQkBCWNoYWluTWV0YQEFB2NoYWluSWQEC2NoYWluSGVpZ2h0CAUNJHQwMjg3MzUyODc4OQJfMQQOY2hhaW5MYXN0QmxvY2sIBQ0kdDAyODczNTI4Nzg5Al8yBA5jaGVja1JlZmVyZW5jZQkBEmlzUmVmZXJlbmNlQ29ycmVjdAIFDHJlZmVyZW5jZUhleAUOY2hhaW5MYXN0QmxvY2sDCQAAAgUOY2hlY2tSZWZlcmVuY2UFDmNoZWNrUmVmZXJlbmNlBA5jaGVja1RyYW5zZmVycwkBFmVuc3VyZUNvcnJlY3RUcmFuc2ZlcnMDCAkBCWJsb2NrTWV0YQEFDHJlZmVyZW5jZUhleAJfNwUXbGFzdENsVG9FbFRyYW5zZmVySW5kZXgGAwkAAAIFDmNoZWNrVHJhbnNmZXJzBQ5jaGVja1RyYW5zZmVycwQObmV3Q2hhaW5IZWlnaHQJAGQCBQtjaGFpbkhlaWdodAABBAlwcmV2RXBvY2gICQEJYmxvY2tNZXRhAQUMcmVmZXJlbmNlSGV4Al8yBBN1cGRhdGVNYWluQ2hhaW5EYXRhAwkAZgIJARFzdXBwb3J0aW5nQmFsYW5jZQEFB2NoYWluSWQJAGkCBRRjb21wdXRlZFRvdGFsQmFsYW5jZQACBAtsYXN0Q2hhaW5JZAkBC3ZhbHVlT3JFbHNlAgkAnwgBBQ5sYXN0Q2hhaW5JZEtleQAABBR1cGRhdGVGaW5hbGl6ZWRCbG9jawkBHWdldFVwZGF0ZUZpbmFsaXplZEJsb2NrQWN0aW9uAwgFAWkMb3JpZ2luQ2FsbGVyBQxibG9ja0hhc2hIZXgFCXByZXZFcG9jaAkAzggCCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEUY2hhaW5Gb3JrZWRIZWlnaHRLZXkBBQttYWluQ2hhaW5JZAgFE2NoYWluRmlyc3RCbG9ja01ldGECXzEJAMwIAgkBDEludGVnZXJFbnRyeQIFDm1haW5DaGFpbklkS2V5BQdjaGFpbklkCQDMCAIJAQxJbnRlZ2VyRW50cnkCBRdmaXJzdFZhbGlkQWx0Q2hhaW5JZEtleQkAZAIFC2xhc3RDaGFpbklkAAEFA25pbAUUdXBkYXRlRmluYWxpemVkQmxvY2sFA25pbAQNdGhpc0Vwb2NoTWV0YQQHJG1hdGNoMAkBCWVwb2NoTWV0YQEFBmhlaWdodAMJAAECBQckbWF0Y2gwAgRVbml0CQELU3RyaW5nRW50cnkCCQEMZXBvY2hNZXRhS2V5AQUGaGVpZ2h0CQCsAgIJAKwCAgkArAICCQCsAgIJAKUIAQgFAWkMb3JpZ2luQ2FsbGVyBQNTRVAJAKQDAQUJcHJldkVwb2NoBQNTRVAFDGJsb2NrSGFzaEhleAQFb3RoZXIFByRtYXRjaDAJAAIBAhVFcG9jaCBhbHJlYWR5IHN0YXJ0ZWQDCQAAAgUNdGhpc0Vwb2NoTWV0YQUNdGhpc0Vwb2NoTWV0YQQddXBkYXRlTWFpbkNoYWluTGFzdE1pbmVkQmxvY2sDAwkAAAIFE3VwZGF0ZU1haW5DaGFpbkRhdGEFA25pbAkBAiE9AgkBC3ZhbHVlT3JFbHNlAgkBDG1pbmVyQ2hhaW5JZAEIBQFpDG9yaWdpbkNhbGxlcgAABQdjaGFpbklkBwkAzAgCCQEMSW50ZWdlckVudHJ5AgkBEmNoYWluTGFzdEhlaWdodEtleQIFC21haW5DaGFpbklkCAUBaQxvcmlnaW5DYWxsZXIIBRNjaGFpbkZpcnN0QmxvY2tNZXRhAl8xBQNuaWwFA25pbAkAzggCCQDOCAIJAM4IAgkAzAgCCQEQbWtCbG9ja01ldGFFbnRyeQcFDGJsb2NrSGFzaEhleAUObmV3Q2hhaW5IZWlnaHQFDHJlZmVyZW5jZUhleAgFAWkMb3JpZ2luQ2FsbGVyBQdjaGFpbklkBRplbFRvQ2xUcmFuc2ZlcnNSb290SGFzaEhleAUXbGFzdENsVG9FbFRyYW5zZmVySW5kZXgJAMwIAgkBEG1rQ2hhaW5NZXRhRW50cnkDBQdjaGFpbklkBQ5uZXdDaGFpbkhlaWdodAUMYmxvY2tIYXNoSGV4CQDMCAIFDXRoaXNFcG9jaE1ldGEJAMwIAgkBDEludGVnZXJFbnRyeQIJAQ9taW5lckNoYWluSWRLZXkBCAUBaQxvcmlnaW5DYWxsZXIFB2NoYWluSWQJAMwIAgkBDEludGVnZXJFbnRyeQIJARJjaGFpbkxhc3RIZWlnaHRLZXkCBQdjaGFpbklkCAUBaQxvcmlnaW5DYWxsZXIFDm5ld0NoYWluSGVpZ2h0BQNuaWwFE3VwZGF0ZU1haW5DaGFpbkRhdGEJAQxhZGRTdXBwb3J0ZXICBQdjaGFpbklkCAUBaQxvcmlnaW5DYWxsZXIFHXVwZGF0ZU1haW5DaGFpbkxhc3RNaW5lZEJsb2NrCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBBGpvaW4BDXJld2FyZEFkZHJlc3MKAR1lbnN1cmVOb3RPdmVycmlkZU90aGVyTWluZXJQawEMZWxBZGRyZXNzSGV4BAckbWF0Y2gwCQChCAEJAQptaW5lclBrS2V5AQUMZWxBZGRyZXNzSGV4AwkAAQIFByRtYXRjaDACCkJ5dGVWZWN0b3IEAnBrBQckbWF0Y2gwAwkAAAIFAnBrCAUBaRVvcmlnaW5DYWxsZXJQdWJsaWNLZXkFBHVuaXQJAAIBCQCsAgIJAKwCAgkArAICAhFFTCBtaW5lciBhZGRyZXNzIAUMZWxBZGRyZXNzSGV4AhggaXMgYWxyZWFkeSBsaW5rZWQgd2l0aCAJANgEAQUCcGsFBHVuaXQDCQEBIQEJAQ9pc0NvbnRyYWN0U2V0dXAACQACAQIfVGhlIGNvbnRyYWN0IGhhcyBub3QgeWV0IHNldCB1cAMJAGYCBQtNSU5fQkFMQU5DRQgJAO8HAQgFAWkMb3JpZ2luQ2FsbGVyCmdlbmVyYXRpbmcJAAIBCQCsAgIJAKwCAgkArAICAiFJbnN1ZmZpY2llbnQgZ2VuZXJhdGluZyBiYWxhbmNlOiAJAKQDAQgJAO8HAQgFAWkMb3JpZ2luQ2FsbGVyCmdlbmVyYXRpbmcCDC4gUmVxdWlyZWQ6IAkApAMBBQtNSU5fQkFMQU5DRQMJAQIhPQIJAMgBAQUNcmV3YXJkQWRkcmVzcwAUCQACAQIlcmV3YXJkQWRkcmVzcyBzaG91bGQgYmUgYW4gTDIgYWRkcmVzcwMJAGcCCQCQAwEFCWFsbE1pbmVycwAyCQACAQIPdG9vIG1hbnkgbWluZXJzCgEOY2hlY2tFeGlzdGVuY2UCBmV4aXN0cwVtaW5lcgMFBmV4aXN0cwYJAAACBQVtaW5lcgkApQgBCAUBaQxvcmlnaW5DYWxsZXIEDWFscmVhZHlFeGlzdHMKAAIkbAUJYWxsTWluZXJzCgACJHMJAJADAQUCJGwKAAUkYWNjMAcKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBDmNoZWNrRXhpc3RlbmNlAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhRMaXN0IHNpemUgZXhjZWVkcyA1MAkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIDBQ1hbHJlYWR5RXhpc3RzBQNuaWwECG5ld01pbmVyCQClCAEIBQFpDG9yaWdpbkNhbGxlcgQQcmV3YXJkQWRkcmVzc0hleAkA3AQBBQ1yZXdhcmRBZGRyZXNzBAVjaGVjawkBHWVuc3VyZU5vdE92ZXJyaWRlT3RoZXJNaW5lclBrAQUQcmV3YXJkQWRkcmVzc0hleAMJAAACBQVjaGVjawUFY2hlY2sECW5ld01pbmVycwMJAAACCQCQAwEFCWFsbE1pbmVycwAABQhuZXdNaW5lcgkArAICCQCsAgIFDGFsbE1pbmVyc1N0cgUDU0VQBQhuZXdNaW5lcgQYZGVsZXRlT2xkUmV3YXJkQWRkcmVzc1BrBAckbWF0Y2gwCQCiCAEJARVtaW5lclJld2FyZEFkZHJlc3NLZXkBBQhuZXdNaW5lcgMJAAECBQckbWF0Y2gwAgZTdHJpbmcECm9sZEFkZHJlc3MFByRtYXRjaDADCQAAAgUKb2xkQWRkcmVzcwkA3AQBBQ1yZXdhcmRBZGRyZXNzBQNuaWwJAMwIAgkBC0RlbGV0ZUVudHJ5AQkBCm1pbmVyUGtLZXkBBQpvbGRBZGRyZXNzBQNuaWwFA25pbAkAzggCCQDMCAIJAQtTdHJpbmdFbnRyeQIFDGFsbE1pbmVyc0tleQUJbmV3TWluZXJzCQDMCAIJAQtTdHJpbmdFbnRyeQIJARVtaW5lclJld2FyZEFkZHJlc3NLZXkBBQhuZXdNaW5lcgkArAICAgIweAUQcmV3YXJkQWRkcmVzc0hleAkAzAgCCQELQmluYXJ5RW50cnkCCQEKbWluZXJQa0tleQEFEHJld2FyZEFkZHJlc3NIZXgIBQFpFW9yaWdpbkNhbGxlclB1YmxpY0tleQUDbmlsBRhkZWxldGVPbGRSZXdhcmRBZGRyZXNzUGsJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4BaQEFbGVhdmUABAxsZWF2aW5nTWluZXIJAKUIAQgFAWkMb3JpZ2luQ2FsbGVyCgEQc2tpcExlYXZpbmdNaW5lcgIDYWNjBW1pbmVyAwkAAAIFBW1pbmVyBQxsZWF2aW5nTWluZXIFA2FjYwkAzQgCBQNhY2MFBW1pbmVyBA9yZW1haW5pbmdNaW5lcnMKAAIkbAUJYWxsTWluZXJzCgACJHMJAJADAQUCJGwKAAUkYWNjMAUDbmlsCgEFJGYwXzECAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJARBza2lwTGVhdmluZ01pbmVyAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhRMaXN0IHNpemUgZXhjZWVkcyA1MAkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIEDXJld2FyZEFkZHJLZXkJARVtaW5lclJld2FyZEFkZHJlc3NLZXkBBQxsZWF2aW5nTWluZXIEEXByZXZSZXdhcmRBZGRyZXNzCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJ0IAgUEdGhpcwUNcmV3YXJkQWRkcktleQIWbWluZXIgaGFzIG5ldmVyIGpvaW5lZAMJAAACBQ50aGlzRXBvY2hNaW5lcggFAWkMb3JpZ2luQ2FsbGVyCQACAQIcZGVzaWduYXRlZCBtaW5lciBjYW4ndCBsZWF2ZQkAzAgCCQELU3RyaW5nRW50cnkCBQxhbGxNaW5lcnNLZXkJALoJAgUPcmVtYWluaW5nTWluZXJzBQNTRVAFA25pbAFpAQh0cmFuc2ZlcgEQZGVzdEVsQWRkcmVzc0hleAQLY2hlY2tDbFRvRWwJAR1yZXF1aXJlQ2xUb0VsVHJhbnNmZXJzRW5hYmxlZAADCQAAAgULY2hlY2tDbFRvRWwFC2NoZWNrQ2xUb0VsBAxjaGVja1BheW1lbnQDCQAAAgkAkAMBCAUBaQhwYXltZW50cwABBgkAAgEJAKwCAgIvRXhwZWN0ZWQgb25lIHBheW1lbnQgYXMgYSB0cmFuc2ZlciBhbW91bnQsIGdvdCAJAKQDAQkAkAMBCAUBaQhwYXltZW50cwMJAAACBQxjaGVja1BheW1lbnQFDGNoZWNrUGF5bWVudAQKdG9rZW5JZFN0cgkBEUBleHRyTmF0aXZlKDEwNTgpAQUKdG9rZW5JZEtleQQHdG9rZW5JZAkA2QQBBQp0b2tlbklkU3RyBAF0CQCRAwIIBQFpCHBheW1lbnRzAAAEDGNoZWNrQXNzZXRJZAQHJG1hdGNoMAgFAXQHYXNzZXRJZAMJAAECBQckbWF0Y2gwAgpCeXRlVmVjdG9yBAdhc3NldElkBQckbWF0Y2gwAwkAAAIFB2Fzc2V0SWQFB3Rva2VuSWQGCQACAQkArAICCQCsAgIJAKwCAgIJRXhwZWN0ZWQgBQp0b2tlbklkU3RyAhUgaW4gdGhlIHBheW1lbnQsIGdvdCAJANgEAQUHYXNzZXRJZAkAAgEJAKwCAgkArAICAglFeHBlY3RlZCAFCnRva2VuSWRTdHICGiBpbiB0aGUgcGF5bWVudCwgZ290IFdhdmVzAwkAAAIFDGNoZWNrQXNzZXRJZAUMY2hlY2tBc3NldElkBAlsYXN0SW5kZXgICQEJYmxvY2tNZXRhAQUSbWFpbkNoYWluTGFzdEJsb2NrAl83BAlxdWV1ZVNpemUJAGQCCQBlAgUUbmF0aXZlVHJhbnNmZXJzQ291bnQFCWxhc3RJbmRleAABBA9jaGVja1F1ZXVlTGltaXQDCQBmAgCgAQUJcXVldWVTaXplCQEKYW1vdW50R3RFcQMFAXQAwIQ9BQlxdWV1ZVNpemUDCQBmAgDADAUJcXVldWVTaXplCQEKYW1vdW50R3RFcQMFAXQAgK3iBAUJcXVldWVTaXplAwkAZgIAgBkFCXF1ZXVlU2l6ZQkBCmFtb3VudEd0RXEDBQF0AIDC1y8FCXF1ZXVlU2l6ZQMJAGYCAIAyBQlxdWV1ZVNpemUJAQphbW91bnRHdEVxAwUBdACAlOvcAwUJcXVldWVTaXplCQACAQkArAICCQCsAgICI1RyYW5zZmVycyBkZW5pZWQgZm9yIHF1ZXVlIHNpemUgb2YgCQCkAwEFCXF1ZXVlU2l6ZQIoLiBXYWl0IHVudGlsIGN1cnJlbnQgdHJhbnNmZXJzIHByb2Nlc3NlZAMJAAACBQ9jaGVja1F1ZXVlTGltaXQFD2NoZWNrUXVldWVMaW1pdAkAzAgCCQEMSW50ZWdlckVudHJ5AgUXbmF0aXZlVHJhbnNmZXJzQ291bnRLZXkJAGQCBRRuYXRpdmVUcmFuc2ZlcnNDb3VudAABCQDMCAIJARVta05hdGl2ZVRyYW5zZmVyRW50cnkDBRRuYXRpdmVUcmFuc2ZlcnNDb3VudAUQZGVzdEVsQWRkcmVzc0hleAgFAXQGYW1vdW50CQDMCAIJAQRCdXJuAgUHdG9rZW5JZAgFAXQGYW1vdW50BQNuaWwJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4BaQEId2l0aGRyYXcEDGJsb2NrSGFzaEhleAttZXJrbGVQcm9vZhR0cmFuc2ZlckluZGV4SW5CbG9jawZhbW91bnQEEXdpdGhkcmF3QmxvY2tNZXRhCQEJYmxvY2tNZXRhAQUMYmxvY2tIYXNoSGV4BBN3aXRoZHJhd0Jsb2NrSGVpZ2h0CAURd2l0aGRyYXdCbG9ja01ldGECXzEEFGZpbmFsaXplZEJsb2NrSGVpZ2h0CAkBCWJsb2NrTWV0YQEJARFAZXh0ck5hdGl2ZSgxMDU4KQEFEWZpbmFsaXplZEJsb2NrS2V5Al8xBBhtYWluQ2hhaW5MYXN0QmxvY2tIZWlnaHQICQEJYmxvY2tNZXRhAQUSbWFpbkNoYWluTGFzdEJsb2NrAl8xAwkAZgIFE3dpdGhkcmF3QmxvY2tIZWlnaHQFFGZpbmFsaXplZEJsb2NrSGVpZ2h0CQACAQkArAICCQCsAgIJAKwCAgIKRUwgYmxvY2sgIwkApAMBBRN3aXRoZHJhd0Jsb2NrSGVpZ2h0Ai0gaXMgbm90IGZpbmFsaXplZC4gVGhlIGN1cnJlbnQgZmluYWxpemVkIGlzICMJAKQDAQUUZmluYWxpemVkQmxvY2tIZWlnaHQEFHdpdGhkcmF3QmxvY2tDaGFpbklkCAURd2l0aGRyYXdCbG9ja01ldGECXzUEC2lzTWFpbkNoYWluCQAAAgUUd2l0aGRyYXdCbG9ja0NoYWluSWQFC21haW5DaGFpbklkBBJyZWxhdGVzVG9NYWluQ2hhaW4EByRtYXRjaDAJAJ8IAQkBFGNoYWluRm9ya2VkSGVpZ2h0S2V5AQUUd2l0aGRyYXdCbG9ja0NoYWluSWQDCQABAgUHJG1hdGNoMAIDSW50BAxmb3JrZWRIZWlnaHQFByRtYXRjaDAJAGYCBQxmb3JrZWRIZWlnaHQFE3dpdGhkcmF3QmxvY2tIZWlnaHQJAAIBCQCsAgIJAKwCAgkArAICBQxibG9ja0hhc2hIZXgCHSBpcyBvbiBhbiBhbHRlcm5hdGl2ZSBjaGFpbiAjCQCkAwEFFHdpdGhkcmF3QmxvY2tDaGFpbklkAjggdGhhdCB3YXMgbm90IGFwcHJvdmVkIGJ5IG1ham9yaXR5LiBXYWl0IGZvciBzb21lIGJsb2NrcwMDBQtpc01haW5DaGFpbgYFEnJlbGF0ZXNUb01haW5DaGFpbgQJcmVjaXBpZW50CAUBaQxvcmlnaW5DYWxsZXIED3JlY2lwaWVudFBrSGFzaAkAyQECCQDKAQIIBQlyZWNpcGllbnQFYnl0ZXMAAgUUUFVCTElDX0tFWV9IQVNIX1NJWkUED3plcm9BbW91bnRCeXRlcwEsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEC2Ftb3VudEJ5dGVzCQCaAwEFBmFtb3VudAQLZWxFdmVudERhdGEJAMsBAgkAywECBQ9yZWNpcGllbnRQa0hhc2gJAMkBAgUPemVyb0Ftb3VudEJ5dGVzCQBlAgkAyAEBBQ96ZXJvQW1vdW50Qnl0ZXMJAMgBAQULYW1vdW50Qnl0ZXMFC2Ftb3VudEJ5dGVzBBFlbEV2ZW50RGF0YURpZ2VzdAkA8BUBBQtlbEV2ZW50RGF0YQQSY2FsY3VsYXRlZFJvb3RIYXNoCQC9BQMFC21lcmtsZVByb29mBRFlbEV2ZW50RGF0YURpZ2VzdAUUdHJhbnNmZXJJbmRleEluQmxvY2sEEGV4cGVjdGVkUm9vdEhhc2gIBRF3aXRoZHJhd0Jsb2NrTWV0YQJfNgMJAAACBRJjYWxjdWxhdGVkUm9vdEhhc2gFEGV4cGVjdGVkUm9vdEhhc2gEB3Rva2VuSWQJANkEAQkBEUBleHRyTmF0aXZlKDEwNTgpAQUKdG9rZW5JZEtleQQMdHJhbnNmZXJzS2V5CQEXYmxvY2tFbFRvQ2xUcmFuc2ZlcnNLZXkBBQxibG9ja0hhc2hIZXgJAMwIAgkBB1JlaXNzdWUDBQd0b2tlbklkBQZhbW91bnQGCQDMCAIJAQ5TY3JpcHRUcmFuc2ZlcgMFCXJlY2lwaWVudAUGYW1vdW50BQd0b2tlbklkCQDMCAIJAQtTdHJpbmdFbnRyeQIFDHRyYW5zZmVyc0tleQkBCXNldE9yRmFpbAIJAQt2YWx1ZU9yRWxzZQIJAKIIAQUMdHJhbnNmZXJzS2V5AgAFFHRyYW5zZmVySW5kZXhJbkJsb2NrBQNuaWwJAAIBCQCsAgIJAKwCAgkArAICCQCsAgIJAKwCAgkArAICAhRFeHBlY3RlZCByb290IGhhc2g6IAkA3AQBBRBleHBlY3RlZFJvb3RIYXNoAgcsIGdvdDogCQDcBAEFEmNhbGN1bGF0ZWRSb290SGFzaAIVLiBFdmVudCBkYXRhIGRpZ2VzdDogCQDaBAEFEWVsRXZlbnREYXRhRGlnZXN0Ah8uIENoZWNrIHlvdXIgd2l0aGRyYXcgYXJndW1lbnRzCQACAQkArAICCQCsAgICCUV4cGVjdGVkIAUMYmxvY2tIYXNoSGV4Ai8gdG8gYmUgZWl0aGVyIG9uIHRoZSBtYWluIGNoYWluIG9yIHJlbGF0ZSB0byBpdAFpAQVzZXR1cAQTZ2VuZXNpc0Jsb2NrSGFzaEhleBFtaW5lclJld2FyZEluR3dlaRlzdGFraW5nQ29udHJhY3RBZGRyZXNzQjU4EmVsQnJpZGdlQWRkcmVzc0hleAMJAQ9pc0NvbnRyYWN0U2V0dXAACQACAQIkVGhlIGNvbnRyYWN0IGhhcyBiZWVuIGFscmVhZHkgc2V0IHVwAwkAZgIAAAURbWluZXJSZXdhcmRJbkd3ZWkJAAIBAiRUaGUgbWluZXIgcmV3YXJkIG11c3QgYmUgbm9ubmVnYXRpdmUEEGdlbmVzaXNCbG9ja0hhc2gJAN0EAQUTZ2VuZXNpc0Jsb2NrSGFzaEhleAQHZW1wdHlQawEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE2dlbmVzaXNNaW5lckFkZHJlc3MJAKcIAQUHZW1wdHlQawQXZ2VuZXNpc0V0aFJld2FyZEFkZHJlc3MBFAAAAAAAAAAAAAAAAAAAAAAAAAAABBlnZW5lc2lzQmxvY2tSZWZlcmVuY2VIYXNoAkAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwBAVpc3N1ZQkAwggFAgVVTklUMAIMTmF0aXZlIHRva2VuAAAACAYEB3Rva2VuSWQJALgIAQUFaXNzdWUEEGdlbmVzaXNCbG9ja01ldGEJAMsBAgkAywECCQDLAQIJAMsBAgkAywECCQDLAQIJAJoDAQAACQCaAwEFBmhlaWdodAkA3QQBBRlnZW5lc2lzQmxvY2tSZWZlcmVuY2VIYXNoCAUTZ2VuZXNpc01pbmVyQWRkcmVzcwVieXRlcwkAmgMBAAABAAkAmgMBAP///////////wEJAMwIAgkBC0JpbmFyeUVudHJ5AgkArAICBQpibG9ja01ldGFLBRNnZW5lc2lzQmxvY2tIYXNoSGV4BRBnZW5lc2lzQmxvY2tNZXRhCQDMCAIJAQtTdHJpbmdFbnRyeQIJARRjaGFpbkZpcnN0QmxvY2tJZEtleQEAAAUTZ2VuZXNpc0Jsb2NrSGFzaEhleAkAzAgCCQEQbWtDaGFpbk1ldGFFbnRyeQMAAAAABRNnZW5lc2lzQmxvY2tIYXNoSGV4CQDMCAIJAQxJbnRlZ2VyRW50cnkCBQ5taW5lclJld2FyZEtleQURbWluZXJSZXdhcmRJbkd3ZWkJAMwIAgkBC1N0cmluZ0VudHJ5AgUZc3Rha2luZ0NvbnRyYWN0QWRkcmVzc0tleQUZc3Rha2luZ0NvbnRyYWN0QWRkcmVzc0I1OAkAzAgCCQELU3RyaW5nRW50cnkCCQEMZXBvY2hNZXRhS2V5AQUGaGVpZ2h0CQCsAgIJAKwCAgkApQgBBRNnZW5lc2lzTWluZXJBZGRyZXNzAgMsMCwFE2dlbmVzaXNCbG9ja0hhc2hIZXgJAMwIAgkBC1N0cmluZ0VudHJ5AgURZmluYWxpemVkQmxvY2tLZXkFE2dlbmVzaXNCbG9ja0hhc2hIZXgJAMwIAgUFaXNzdWUJAMwIAgkBC1N0cmluZ0VudHJ5AgUKdG9rZW5JZEtleQkA2AQBBQd0b2tlbklkCQDMCAIJAQtTdHJpbmdFbnRyeQIFEmVsQnJpZGdlQWRkcmVzc0tleQkArAICAgIweAUSZWxCcmlkZ2VBZGRyZXNzSGV4CQDMCAIJAQxJbnRlZ2VyRW50cnkCBRdjbFRvRWxUcmFuc2ZlcnNFcG9jaEtleQAABQNuaWwAxmm0zw==", "height": 3246951, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: GPDrfDQ2Jpsvk5nSwYoCMcd5MEHcZSCcD2kGPSNpoy1M Next: 3dyZaun8aXdrUhLxUK6wZAxc6oi3XiTfHxWPo2ysQohP Diff:
Old | New | Differences | |
---|---|---|---|
14 | 14 | let ADDRESS_SIZE = 26 | |
15 | 15 | ||
16 | 16 | let PUBLIC_KEY_HASH_SIZE = 20 | |
17 | + | ||
18 | + | let ROOT_HASH_SIZE = 32 | |
19 | + | ||
20 | + | let MAX_CL_TO_EL_TRANSFERS = 16 | |
17 | 21 | ||
18 | 22 | let zeroesStr = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" | |
19 | 23 | ||
39 | 43 | ||
40 | 44 | let elBridgeAddressKey = "elBridgeAddress" | |
41 | 45 | ||
42 | - | let | |
46 | + | let nativeTransfersCountKey = "nativeTransfersCount" | |
43 | 47 | ||
44 | - | let | |
48 | + | let clToElTransfersEpochKey = "clToElTransfersEpoch" | |
45 | 49 | ||
46 | 50 | func pad (i) = { | |
47 | 51 | let s = toString(i) | |
96 | 100 | func minerChainIdKey (miner) = (("miner_" + toString(miner)) + "_ChainId") | |
97 | 101 | ||
98 | 102 | ||
99 | - | let | |
103 | + | let nativeTransfersCount = valueOrElse(getInteger(this, nativeTransfersCountKey), 0) | |
100 | 104 | ||
101 | - | func requireElToClTransfersDisabled () = if ((elToClTransfersEpoch > height)) | |
102 | - | then unit | |
103 | - | else throw((("EL to CL transfers activated on epoch #" + toString(height)) + ". Upgrade your client")) | |
105 | + | func nativeTransferKey (index) = ("nativeTransfer_" + toString(index)) | |
104 | 106 | ||
105 | 107 | ||
106 | - | func requireElToClTransfersEnabled () = if ((height >= elToClTransfersEpoch)) | |
108 | + | func mkNativeTransferEntry (index,destElAddressHex,amount) = StringEntry(nativeTransferKey(index), ((("0x" + destElAddressHex) + SEP) + toString(amount))) | |
109 | + | ||
110 | + | ||
111 | + | let clToElTransfersEpoch = valueOrElse(getInteger(this, clToElTransfersEpochKey), INT_MAX) | |
112 | + | ||
113 | + | func requireClToElTransfersDisabled () = if ((clToElTransfersEpoch > height)) | |
107 | 114 | then unit | |
108 | - | else throw(((("EL to CL transfers haven't activated on epoch #" + toString(height)) + ". Wait for activation on #") + toString(elToClTransfersEpoch))) | |
115 | + | else throw((("CL to EL transfers activated on epoch #" + toString(height)) + ". Upgrade your client")) | |
116 | + | ||
117 | + | ||
118 | + | func requireClToElTransfersEnabled () = if ((height >= clToElTransfersEpoch)) | |
119 | + | then unit | |
120 | + | else throw(((("CL to EL transfers haven't activated on epoch #" + toString(height)) + ". Wait for activation on #") + toString(clToElTransfersEpoch))) | |
121 | + | ||
122 | + | ||
123 | + | func ensureCorrectTransfers (refTransferIndex,transferIndex,expectReward) = { | |
124 | + | let maxTransfers = if (expectReward) | |
125 | + | then (MAX_CL_TO_EL_TRANSFERS - 1) | |
126 | + | else MAX_CL_TO_EL_TRANSFERS | |
127 | + | let actualTransfers = (transferIndex - refTransferIndex) | |
128 | + | let checkNumber = if ((actualTransfers > maxTransfers)) | |
129 | + | then throw(((("Allowed only " + toString(maxTransfers)) + " transfers, got ") + toString(actualTransfers))) | |
130 | + | else true | |
131 | + | if ((checkNumber == checkNumber)) | |
132 | + | then if ((transferIndex >= nativeTransfersCount)) | |
133 | + | then throw(((("Attempt to transfer #" + toString(transferIndex)) + ". Available transfers: ") + toString(nativeTransfersCount))) | |
134 | + | else true | |
135 | + | else throw("Strict value is not equal to itself.") | |
136 | + | } | |
137 | + | ||
138 | + | ||
139 | + | func amountGtEq (t,gtEq,queueSize) = if ((t.amount >= gtEq)) | |
140 | + | then true | |
141 | + | else throw((((((("Transferring amount " + toString(t.amount)) + " should be >= ") + toString(gtEq)) + " for queue size of ") + toString(queueSize)) + ". Transfer more or wait")) | |
109 | 142 | ||
110 | 143 | ||
111 | 144 | let stakingContractAddress = match getString(this, stakingContractAddressKey) { | |
139 | 172 | } | |
140 | 173 | ||
141 | 174 | ||
175 | + | func mkChainMetaEntry (chainId,newChainHeight,blockHashHex) = StringEntry(chainMetaKey(chainId), ((toString(newChainHeight) + SEP) + blockHashHex)) | |
176 | + | ||
177 | + | ||
142 | 178 | let mainChainId = valueOrElse(getInteger(mainChainIdKey), 0) | |
143 | 179 | ||
144 | - | let $ | |
180 | + | let $t064236489 = chainMeta(mainChainId) | |
145 | 181 | ||
146 | - | let mainChainHeight = $ | |
182 | + | let mainChainHeight = $t064236489._1 | |
147 | 183 | ||
148 | - | let mainChainLastBlock = $ | |
184 | + | let mainChainLastBlock = $t064236489._2 | |
149 | 185 | ||
150 | 186 | func epochMeta (epoch) = match getString(epochMetaKey(epoch)) { | |
151 | 187 | case s: String => | |
156 | 192 | } | |
157 | 193 | ||
158 | 194 | ||
159 | - | let $ | |
195 | + | let $t067207184 = match epochMeta(height) { | |
160 | 196 | case m: (Address, Int, String) => | |
161 | 197 | m | |
162 | 198 | case _ => | |
172 | 208 | } | |
173 | 209 | } | |
174 | 210 | ||
175 | - | let thisEpochMiner = $ | |
211 | + | let thisEpochMiner = $t067207184._1 | |
176 | 212 | ||
177 | - | let thisEpochRef = $ | |
213 | + | let thisEpochRef = $t067207184._2 | |
178 | 214 | ||
179 | - | let thisEpochLastBlock = $ | |
215 | + | let thisEpochLastBlock = $t067207184._3 | |
180 | 216 | ||
181 | 217 | let allMinersStr = valueOrElse(getString(allMinersKey), "") | |
182 | 218 | ||
194 | 230 | ||
195 | 231 | func blockMeta (blockId) = { | |
196 | 232 | let meta = getBinaryValue((blockMetaK + blockId)) | |
233 | + | let metaSize = size(meta) | |
197 | 234 | let blockHeight = toInt(meta) | |
198 | 235 | let blockEpoch = toInt(meta, 8) | |
199 | 236 | let blockParent = take(drop(meta, 16), BLOCK_HASH_SIZE) | |
200 | 237 | let blockGenerator = take(drop(meta, (16 + BLOCK_HASH_SIZE)), ADDRESS_SIZE) | |
201 | - | if ((elToClTransfersEpoch > blockEpoch)) | |
202 | - | then $Tuple6(blockHeight, blockEpoch, blockParent, blockGenerator, 0, base58'') | |
203 | - | else { | |
204 | - | let chainId = toInt(drop(meta, ((16 + BLOCK_HASH_SIZE) + ADDRESS_SIZE))) | |
205 | - | let elToClTransfersRootHash = drop(meta, ((24 + BLOCK_HASH_SIZE) + ADDRESS_SIZE)) | |
206 | - | $Tuple6(blockHeight, blockEpoch, blockParent, blockGenerator, chainId, elToClTransfersRootHash) | |
207 | - | } | |
238 | + | let chainId = toInt(meta, ((16 + BLOCK_HASH_SIZE) + ADDRESS_SIZE)) | |
239 | + | let baseOffset = ((24 + BLOCK_HASH_SIZE) + ADDRESS_SIZE) | |
240 | + | let remainingBytes = (metaSize - baseOffset) | |
241 | + | let elToClTransfersRootHash = if ((remainingBytes >= ROOT_HASH_SIZE)) | |
242 | + | then take(drop(meta, baseOffset), ROOT_HASH_SIZE) | |
243 | + | else base58'' | |
244 | + | let lastClToElTransferIndex = if (if ((remainingBytes == 8)) | |
245 | + | then true | |
246 | + | else (remainingBytes > ROOT_HASH_SIZE)) | |
247 | + | then toInt(meta, (baseOffset + size(elToClTransfersRootHash))) | |
248 | + | else -1 | |
249 | + | $Tuple7(blockHeight, blockEpoch, blockParent, blockGenerator, chainId, elToClTransfersRootHash, lastClToElTransferIndex) | |
208 | 250 | } | |
209 | 251 | ||
210 | 252 | ||
211 | - | func mkBlockMetaEntry (blockHashHex,blockHeight,blockParentHex,blockGenerator,chainId,elToClTransfersRootHashHex) = { | |
253 | + | func mkBlockMetaEntry (blockHashHex,blockHeight,blockParentHex,blockGenerator,chainId,elToClTransfersRootHashHex,lastClToElTransferIndex) = { | |
212 | 254 | let blockEpochBytes = toBytes(height) | |
213 | - | let blockMetaBytes = (((((toBytes(blockHeight) + blockEpochBytes) + fromBase16String(blockParentHex)) + blockGenerator.bytes) + toBytes(chainId)) + fromBase16String(elToClTransfersRootHashHex)) | |
214 | - | BinaryEntry((blockMetaK + blockHashHex), blockMetaBytes) | |
255 | + | let blockMetaBytes1 = (((((toBytes(blockHeight) + blockEpochBytes) + fromBase16String(blockParentHex)) + blockGenerator.bytes) + toBytes(chainId)) + fromBase16String(elToClTransfersRootHashHex)) | |
256 | + | let blockMetaBytes2 = if ((clToElTransfersEpoch > height)) | |
257 | + | then base58'' | |
258 | + | else toBytes(lastClToElTransferIndex) | |
259 | + | BinaryEntry((blockMetaK + blockHashHex), (blockMetaBytes1 + blockMetaBytes2)) | |
215 | 260 | } | |
216 | 261 | ||
217 | 262 | ||
224 | 269 | } | |
225 | 270 | ||
226 | 271 | ||
227 | - | let $ | |
272 | + | let $t0924610212 = { | |
228 | 273 | let hitSource = match lastBlock.vrf { | |
229 | 274 | case vrf: ByteVector => | |
230 | 275 | vrf | |
232 | 277 | lastBlock.generationSignature | |
233 | 278 | } | |
234 | 279 | func processMiner (prev,miner) = { | |
235 | - | let $ | |
236 | - | let prevDelay = $ | |
237 | - | let prevMiner = $ | |
238 | - | let prevTotalBalance = $ | |
239 | - | let prevMiners = $ | |
280 | + | let $t095449607 = prev | |
281 | + | let prevDelay = $t095449607._1 | |
282 | + | let prevMiner = $t095449607._2 | |
283 | + | let prevTotalBalance = $t095449607._3 | |
284 | + | let prevMiners = $t095449607._4 | |
240 | 285 | let minerAddress = addressFromStringValue(miner) | |
241 | 286 | let wavesGenBalance = wavesBalance(minerAddress).generating | |
242 | 287 | let minerBalance = generatingBalance(minerAddress) | |
266 | 311 | $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) | |
267 | 312 | } | |
268 | 313 | ||
269 | - | let computedDelay = $ | |
314 | + | let computedDelay = $t0924610212._1 | |
270 | 315 | ||
271 | - | let computedGenerator = $ | |
316 | + | let computedGenerator = $t0924610212._2 | |
272 | 317 | ||
273 | - | let computedTotalBalance = $ | |
318 | + | let computedTotalBalance = $t0924610212._3 | |
274 | 319 | ||
275 | - | let filteredMiners = $ | |
320 | + | let filteredMiners = $t0924610212._4 | |
276 | 321 | ||
277 | 322 | func getChainLastBlockId (chainId) = chainMeta(chainId)._2 | |
278 | 323 | ||
279 | 324 | ||
280 | - | let $ | |
325 | + | let $t01027810385 = blockMeta(mainChainLastBlock) | |
281 | 326 | ||
282 | - | let mclbIgnored1 = $ | |
327 | + | let mclbIgnored1 = $t01027810385._1 | |
283 | 328 | ||
284 | - | let mainChainEpoch = $ | |
329 | + | let mainChainEpoch = $t01027810385._2 | |
285 | 330 | ||
286 | - | let mainChainParentHash = $ | |
331 | + | let mainChainParentHash = $t01027810385._3 | |
287 | 332 | ||
288 | - | let mainChainGenerator = $t083528487._4 | |
289 | - | ||
290 | - | let mclbIgnored2 = $t083528487._5 | |
291 | - | ||
292 | - | let mclbIgnored3 = $t083528487._6 | |
333 | + | let mainChainGenerator = $t01027810385._4 | |
293 | 334 | ||
294 | 335 | func calculateFinalizedBlockHash (curMiner,curPrevEpoch,curLastBlockHash) = { | |
295 | 336 | let offsets_100 = split_4C("::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::", "") | |
296 | 337 | let halfBalance = (computedTotalBalance / 2) | |
297 | 338 | func step (prev,next) = { | |
298 | - | let $ | |
299 | - | let thisEpoch = $ | |
300 | - | let totalBalance = $ | |
301 | - | let maybeSafeEpoch = $ | |
302 | - | let prevMiners = $ | |
339 | + | let $t01075110815 = prev | |
340 | + | let thisEpoch = $t01075110815._1 | |
341 | + | let totalBalance = $t01075110815._2 | |
342 | + | let maybeSafeEpoch = $t01075110815._3 | |
343 | + | let prevMiners = $t01075110815._4 | |
303 | 344 | match maybeSafeEpoch { | |
304 | 345 | case _: Unit => | |
305 | - | let $ | |
346 | + | let $t01087311039 = if ((thisEpoch == height)) | |
306 | 347 | then $Tuple3(curMiner, curPrevEpoch, curLastBlockHash) | |
307 | 348 | else value(epochMeta(thisEpoch)) | |
308 | - | let miner = $ | |
309 | - | let prevEpoch = $ | |
310 | - | let lastBlockHash = $ | |
349 | + | let miner = $t01087311039._1 | |
350 | + | let prevEpoch = $t01087311039._2 | |
351 | + | let lastBlockHash = $t01087311039._3 | |
311 | 352 | if ((prevEpoch == 0)) | |
312 | 353 | then $Tuple4(thisEpoch, totalBalance, lastBlockHash, allMiners) | |
313 | 354 | else { | |
314 | - | let $ | |
355 | + | let $t01116411366 = if (containsElement(prevMiners, miner)) | |
315 | 356 | then $Tuple2(totalBalance, prevMiners) | |
316 | 357 | else $Tuple2((totalBalance + generatingBalance(miner)), miner :: prevMiners) | |
317 | - | let newTotalBalance = $ | |
318 | - | let newMiners = $ | |
358 | + | let newTotalBalance = $t01116411366._1 | |
359 | + | let newMiners = $t01116411366._2 | |
319 | 360 | if ((newTotalBalance > halfBalance)) | |
320 | 361 | then $Tuple4(thisEpoch, newTotalBalance, lastBlockHash, allMiners) | |
321 | 362 | else $Tuple4(prevEpoch, newTotalBalance, unit, newMiners) | |
325 | 366 | } | |
326 | 367 | } | |
327 | 368 | ||
328 | - | let $ | |
369 | + | let $t01159911732 = { | |
329 | 370 | let $l = (offsets_100 ++ offsets_100) | |
330 | 371 | let $s = size($l) | |
331 | 372 | let $acc0 = $Tuple4(height, 0, unit, nil) | |
339 | 380 | ||
340 | 381 | $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($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($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), 101), 102), 103), 104), 105), 106), 107), 108), 109), 110), 111), 112), 113), 114), 115), 116), 117), 118), 119), 120), 121), 122), 123), 124), 125), 126), 127), 128), 129), 130), 131), 132), 133), 134), 135), 136), 137), 138), 139), 140), 141), 142), 143), 144), 145), 146), 147), 148), 149), 150), 151), 152), 153), 154), 155), 156), 157), 158), 159), 160), 161), 162), 163), 164), 165), 166), 167), 168), 169), 170), 171), 172), 173), 174), 175), 176), 177), 178), 179), 180), 181), 182), 183), 184), 185), 186), 187), 188), 189), 190), 191), 192), 193), 194), 195), 196), 197), 198), 199), 200) | |
341 | 382 | } | |
342 | - | let fallbackEpoch = $ | |
343 | - | let totalBalance = $ | |
344 | - | let finalizedBlockHashOpt = $ | |
345 | - | let miners = $ | |
383 | + | let fallbackEpoch = $t01159911732._1 | |
384 | + | let totalBalance = $t01159911732._2 | |
385 | + | let finalizedBlockHashOpt = $t01159911732._3 | |
386 | + | let miners = $t01159911732._4 | |
346 | 387 | match finalizedBlockHashOpt { | |
347 | 388 | case finalizedBlockHash: String => | |
348 | 389 | finalizedBlockHash | |
354 | 395 | ||
355 | 396 | func supportingBalance (chainId) = { | |
356 | 397 | func addBalance (acc,generatorStr) = { | |
357 | - | let $ | |
358 | - | let totalBalance = $ | |
359 | - | let generators = $ | |
398 | + | let $t01200212038 = acc | |
399 | + | let totalBalance = $t01200212038._1 | |
400 | + | let generators = $t01200212038._2 | |
360 | 401 | let generator = addressFromStringValue(generatorStr) | |
361 | 402 | if (containsElement(generators, generator)) | |
362 | 403 | then acc | |
367 | 408 | } | |
368 | 409 | ||
369 | 410 | let allGenerators = split_4C(getStringValue(supportersKey(chainId)), SEP) | |
370 | - | let $ | |
411 | + | let $t01236012425 = { | |
371 | 412 | let $l = allGenerators | |
372 | 413 | let $s = size($l) | |
373 | 414 | let $acc0 = $Tuple2(0, nil) | |
381 | 422 | ||
382 | 423 | $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) | |
383 | 424 | } | |
384 | - | let balance = $ | |
385 | - | let _g = $ | |
425 | + | let balance = $t01236012425._1 | |
426 | + | let _g = $t01236012425._2 | |
386 | 427 | balance | |
387 | 428 | } | |
388 | 429 | ||
490 | 531 | ||
491 | 532 | ||
492 | 533 | @Callable(i) | |
493 | - | func appendBlock (blockHashHex,referenceHex) = { | |
494 | - | let | |
495 | - | if (( | |
534 | + | func appendBlock_v2 (blockHashHex,referenceHex,elToClTransfersRootHashHex) = { | |
535 | + | let checkClToEl = requireClToElTransfersDisabled() | |
536 | + | if ((checkClToEl == checkClToEl)) | |
496 | 537 | then if ((thisEpochMiner != i.originCaller)) | |
497 | 538 | then match thisEpochMiner { | |
498 | 539 | case epochMiner: Address => | |
502 | 543 | } | |
503 | 544 | else { | |
504 | 545 | let chainId = valueOrElse(minerChainId(i.originCaller), mainChainId) | |
505 | - | let $ | |
506 | - | let chainHeight = $ | |
507 | - | let lastBlockId = $ | |
546 | + | let $t01658216633 = chainMeta(chainId) | |
547 | + | let chainHeight = $t01658216633._1 | |
548 | + | let lastBlockId = $t01658216633._2 | |
508 | 549 | let checkReference = isReferenceCorrect(referenceHex, lastBlockId) | |
509 | 550 | if ((checkReference == checkReference)) | |
510 | 551 | then { | |
511 | 552 | let newChainHeight = (chainHeight + 1) | |
512 | - | let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(lastBlockId)) + i.originCaller.bytes) | |
513 | 553 | let checkBlockHash = validateBlockHash(blockHashHex) | |
514 | 554 | if ((checkBlockHash == checkBlockHash)) | |
515 | - | then [ | |
555 | + | then [mkBlockMetaEntry(blockHashHex, newChainHeight, lastBlockId, i.originCaller, chainId, elToClTransfersRootHashHex, -1), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight), mkChainMetaEntry(chainId, newChainHeight, blockHashHex), StringEntry(epochMetaKey(height), ((((toString(value(thisEpochMiner)) + SEP) + toString(thisEpochRef)) + SEP) + blockHashHex))] | |
516 | 556 | else throw("Strict value is not equal to itself.") | |
517 | 557 | } | |
518 | 558 | else throw("Strict value is not equal to itself.") | |
523 | 563 | ||
524 | 564 | ||
525 | 565 | @Callable(i) | |
526 | - | func appendBlock_v2 (blockHashHex,referenceHex,elToClTransfersRootHashHex) = { | |
527 | - | let checkVersion = requireElToClTransfersEnabled() | |
528 | - | if ((checkVersion == checkVersion)) | |
529 | - | then if ((thisEpochMiner != i.originCaller)) | |
530 | - | then match thisEpochMiner { | |
531 | - | case epochMiner: Address => | |
532 | - | throw(("not allowed to forge blocks in this epoch, expected from " + toString(epochMiner))) | |
533 | - | case _ => | |
534 | - | throw("not allowed to forge blocks in this epoch, epoch miner is absent") | |
566 | + | func appendBlock_v3 (blockHashHex,referenceHex,elToClTransfersRootHashHex,lastClToElTransferIndex) = { | |
567 | + | let checkElToCl = requireClToElTransfersEnabled() | |
568 | + | if ((checkElToCl == checkElToCl)) | |
569 | + | then { | |
570 | + | let checkCaller = if ((thisEpochMiner == i.originCaller)) | |
571 | + | then true | |
572 | + | else match thisEpochMiner { | |
573 | + | case epochMiner: Address => | |
574 | + | throw(("not allowed to forge blocks in this epoch, expected from " + toString(epochMiner))) | |
575 | + | case _ => | |
576 | + | throw("not allowed to forge blocks in this epoch, epoch miner is absent") | |
577 | + | } | |
578 | + | if ((checkCaller == checkCaller)) | |
579 | + | then { | |
580 | + | let chainId = valueOrElse(minerChainId(i.originCaller), mainChainId) | |
581 | + | let $t01786017911 = chainMeta(chainId) | |
582 | + | let chainHeight = $t01786017911._1 | |
583 | + | let lastBlockId = $t01786017911._2 | |
584 | + | let checkReference = isReferenceCorrect(referenceHex, lastBlockId) | |
585 | + | if ((checkReference == checkReference)) | |
586 | + | then { | |
587 | + | let checkTransfers = ensureCorrectTransfers(blockMeta(referenceHex)._7, lastClToElTransferIndex, false) | |
588 | + | if ((checkTransfers == checkTransfers)) | |
589 | + | then { | |
590 | + | let newChainHeight = (chainHeight + 1) | |
591 | + | let checkBlockHash = validateBlockHash(blockHashHex) | |
592 | + | if ((checkBlockHash == checkBlockHash)) | |
593 | + | then [mkBlockMetaEntry(blockHashHex, newChainHeight, lastBlockId, i.originCaller, chainId, elToClTransfersRootHashHex, lastClToElTransferIndex), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight), mkChainMetaEntry(chainId, newChainHeight, blockHashHex), StringEntry(epochMetaKey(height), ((((toString(value(thisEpochMiner)) + SEP) + toString(thisEpochRef)) + SEP) + blockHashHex))] | |
594 | + | else throw("Strict value is not equal to itself.") | |
595 | + | } | |
596 | + | else throw("Strict value is not equal to itself.") | |
597 | + | } | |
598 | + | else throw("Strict value is not equal to itself.") | |
599 | + | } | |
600 | + | else throw("Strict value is not equal to itself.") | |
535 | 601 | } | |
536 | - | else { | |
537 | - | let chainId = valueOrElse(minerChainId(i.originCaller), mainChainId) | |
538 | - | let $t01596916020 = chainMeta(chainId) | |
539 | - | let chainHeight = $t01596916020._1 | |
540 | - | let lastBlockId = $t01596916020._2 | |
541 | - | let checkReference = isReferenceCorrect(referenceHex, lastBlockId) | |
542 | - | if ((checkReference == checkReference)) | |
543 | - | then { | |
544 | - | let newChainHeight = (chainHeight + 1) | |
545 | - | let checkBlockHash = validateBlockHash(blockHashHex) | |
546 | - | if ((checkBlockHash == checkBlockHash)) | |
547 | - | then [mkBlockMetaEntry(blockHashHex, newChainHeight, lastBlockId, i.originCaller, chainId, elToClTransfersRootHashHex), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight), StringEntry(chainMetaKey(chainId), ((toString(newChainHeight) + SEP) + blockHashHex)), StringEntry(epochMetaKey(height), ((((toString(value(thisEpochMiner)) + SEP) + toString(thisEpochRef)) + SEP) + blockHashHex))] | |
548 | - | else throw("Strict value is not equal to itself.") | |
549 | - | } | |
550 | - | else throw("Strict value is not equal to itself.") | |
551 | - | } | |
552 | 602 | else throw("Strict value is not equal to itself.") | |
553 | 603 | } | |
554 | 604 | ||
555 | 605 | ||
556 | 606 | ||
557 | 607 | @Callable(i) | |
558 | - | func extendMainChain (blockHashHex,referenceHex,epoch) = { | |
559 | - | let | |
560 | - | if (( | |
608 | + | func extendMainChain_v2 (blockHashHex,referenceHex,epoch,elToClTransfersRootHashHex) = { | |
609 | + | let checkClToEl = requireClToElTransfersDisabled() | |
610 | + | if ((checkClToEl == checkClToEl)) | |
561 | 611 | then { | |
562 | 612 | let checkBlockHash = validateBlockHash(blockHashHex) | |
563 | 613 | if ((checkBlockHash == checkBlockHash)) | |
582 | 632 | } | |
583 | 633 | let updateFinalizedBlock = getUpdateFinalizedBlockAction(i.originCaller, blockHashHex, mainChainEpoch) | |
584 | 634 | let newChainHeight = (mainChainHeight + 1) | |
585 | - | let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(mainChainLastBlock)) + i.originCaller.bytes) | |
586 | - | ([BinaryEntry((blockMetaK + blockHashHex), newBlockMeta), StringEntry(chainMetaKey(mainChainId), ((toString(newChainHeight) + SEP) + blockHashHex)), IntegerEntry(minerChainIdKey(i.originCaller), mainChainId), IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), newChainHeight), thisEpochMeta] ++ updateFinalizedBlock) | |
635 | + | ([mkBlockMetaEntry(blockHashHex, newChainHeight, mainChainLastBlock, i.originCaller, mainChainId, elToClTransfersRootHashHex, -1), mkChainMetaEntry(mainChainId, newChainHeight, blockHashHex), IntegerEntry(minerChainIdKey(i.originCaller), mainChainId), IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), newChainHeight), thisEpochMeta] ++ updateFinalizedBlock) | |
587 | 636 | } | |
588 | 637 | else throw("Strict value is not equal to itself.") | |
589 | 638 | } | |
601 | 650 | ||
602 | 651 | ||
603 | 652 | @Callable(i) | |
604 | - | func extendMainChain_ | |
605 | - | let | |
606 | - | if (( | |
653 | + | func extendMainChain_v3 (blockHashHex,referenceHex,epoch,elToClTransfersRootHashHex,lastClToElTransferIndex) = { | |
654 | + | let checkElToCl = requireClToElTransfersEnabled() | |
655 | + | if ((checkElToCl == checkElToCl)) | |
607 | 656 | then { | |
608 | 657 | let checkBlockHash = validateBlockHash(blockHashHex) | |
609 | 658 | if ((checkBlockHash == checkBlockHash)) | |
620 | 669 | let checkReference = isReferenceCorrect(referenceHex, mainChainLastBlock) | |
621 | 670 | if ((checkReference == checkReference)) | |
622 | 671 | then { | |
623 | - | let thisEpochMeta = match epochMeta(height) { | |
624 | - | case _: Unit => | |
625 | - | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(mainChainEpoch)) + SEP) + blockHashHex)) | |
626 | - | case other => | |
627 | - | throw("Epoch already started") | |
628 | - | } | |
629 | - | let updateFinalizedBlock = getUpdateFinalizedBlockAction(i.originCaller, blockHashHex, mainChainEpoch) | |
630 | - | let newChainHeight = (mainChainHeight + 1) | |
631 | - | ([mkBlockMetaEntry(blockHashHex, newChainHeight, mainChainLastBlock, i.originCaller, mainChainId, elToClTransfersRootHashHex), StringEntry(chainMetaKey(mainChainId), ((toString(newChainHeight) + SEP) + blockHashHex)), IntegerEntry(minerChainIdKey(i.originCaller), mainChainId), IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), newChainHeight), thisEpochMeta] ++ updateFinalizedBlock) | |
672 | + | let checkTransfers = ensureCorrectTransfers(blockMeta(referenceHex)._7, lastClToElTransferIndex, true) | |
673 | + | if ((checkTransfers == checkTransfers)) | |
674 | + | then { | |
675 | + | let thisEpochMeta = match epochMeta(height) { | |
676 | + | case _: Unit => | |
677 | + | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(mainChainEpoch)) + SEP) + blockHashHex)) | |
678 | + | case other => | |
679 | + | throw("Epoch already started") | |
680 | + | } | |
681 | + | if ((thisEpochMeta == thisEpochMeta)) | |
682 | + | then { | |
683 | + | let updateFinalizedBlock = getUpdateFinalizedBlockAction(i.originCaller, blockHashHex, mainChainEpoch) | |
684 | + | let newChainHeight = (mainChainHeight + 1) | |
685 | + | ([mkBlockMetaEntry(blockHashHex, newChainHeight, mainChainLastBlock, i.originCaller, mainChainId, elToClTransfersRootHashHex, lastClToElTransferIndex), mkChainMetaEntry(mainChainId, newChainHeight, blockHashHex), IntegerEntry(minerChainIdKey(i.originCaller), mainChainId), IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), newChainHeight), thisEpochMeta] ++ updateFinalizedBlock) | |
686 | + | } | |
687 | + | else throw("Strict value is not equal to itself.") | |
688 | + | } | |
689 | + | else throw("Strict value is not equal to itself.") | |
632 | 690 | } | |
633 | 691 | else throw("Strict value is not equal to itself.") | |
634 | 692 | } | |
646 | 704 | ||
647 | 705 | ||
648 | 706 | @Callable(i) | |
649 | - | func startAltChain (blockHashHex,referenceHex,epoch) = { | |
650 | - | let | |
651 | - | if (( | |
707 | + | func startAltChain_v2 (blockHashHex,referenceHex,epoch,elToClTransfersRootHashHex) = { | |
708 | + | let checkClToEl = requireClToElTransfersDisabled() | |
709 | + | if ((checkClToEl == checkClToEl)) | |
652 | 710 | then { | |
653 | 711 | let checkBlockHash = validateBlockHash(blockHashHex) | |
654 | 712 | if ((checkBlockHash == checkBlockHash)) | |
659 | 717 | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
660 | 718 | if ((checkGenerator == checkGenerator)) | |
661 | 719 | then { | |
662 | - | let $t01969819802 = blockMeta(referenceHex) | |
663 | - | let refChainHeight = $t01969819802._1 | |
664 | - | let refEpoch = $t01969819802._2 | |
665 | - | let refRef = $t01969819802._3 | |
666 | - | let refGenerator = $t01969819802._4 | |
667 | - | let refIgnored1 = $t01969819802._5 | |
668 | - | let refIgnored2 = $t01969819802._6 | |
720 | + | let $t02180521883 = blockMeta(referenceHex) | |
721 | + | let refChainHeight = $t02180521883._1 | |
722 | + | let refEpoch = $t02180521883._2 | |
723 | + | let refRef = $t02180521883._3 | |
724 | + | let refGenerator = $t02180521883._4 | |
669 | 725 | let finalizedEpoch = blockMeta(getStringValue(finalizedBlockKey))._2 | |
670 | 726 | let epochRef = if ((refEpoch >= finalizedEpoch)) | |
671 | 727 | then refEpoch | |
675 | 731 | then { | |
676 | 732 | let newChainId = (valueOrElse(getInteger(lastChainIdKey), 0) + 1) | |
677 | 733 | let newChainHeight = (blockMeta(referenceHex)._1 + 1) | |
678 | - | let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(referenceHex)) + i.originCaller.bytes) | |
679 | 734 | let thisEpochMeta = match epochMeta(height) { | |
680 | 735 | case _: Unit => | |
681 | 736 | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(epochRef)) + SEP) + blockHashHex)) | |
682 | 737 | case other => | |
683 | 738 | throw("Epoch already started") | |
684 | 739 | } | |
685 | - | [thisEpochMeta, | |
740 | + | [thisEpochMeta, mkBlockMetaEntry(blockHashHex, newChainHeight, referenceHex, i.originCaller, newChainId, elToClTransfersRootHashHex, -1), 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)] | |
686 | 741 | } | |
687 | 742 | else throw("Strict value is not equal to itself.") | |
688 | 743 | } | |
698 | 753 | ||
699 | 754 | ||
700 | 755 | @Callable(i) | |
701 | - | func startAltChain_ | |
702 | - | let | |
703 | - | if (( | |
756 | + | func startAltChain_v3 (blockHashHex,referenceHex,epoch,elToClTransfersRootHashHex,lastClToElTransferIndex) = { | |
757 | + | let checkElToCl = requireClToElTransfersEnabled() | |
758 | + | if ((checkElToCl == checkElToCl)) | |
704 | 759 | then { | |
705 | 760 | let checkBlockHash = validateBlockHash(blockHashHex) | |
706 | 761 | if ((checkBlockHash == checkBlockHash)) | |
711 | 766 | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
712 | 767 | if ((checkGenerator == checkGenerator)) | |
713 | 768 | then { | |
714 | - | let $t02161221716 = blockMeta(referenceHex) | |
715 | - | let refChainHeight = $t02161221716._1 | |
716 | - | let refEpoch = $t02161221716._2 | |
717 | - | let refRef = $t02161221716._3 | |
718 | - | let refGenerator = $t02161221716._4 | |
719 | - | let refIgnored1 = $t02161221716._5 | |
720 | - | let refIgnored2 = $t02161221716._6 | |
769 | + | let $t02364123763 = blockMeta(referenceHex) | |
770 | + | let refChainHeight = $t02364123763._1 | |
771 | + | let refEpoch = $t02364123763._2 | |
772 | + | let refRef = $t02364123763._3 | |
773 | + | let refGenerator = $t02364123763._4 | |
774 | + | let refIgnored5 = $t02364123763._5 | |
775 | + | let refIgnored6 = $t02364123763._6 | |
776 | + | let refTransferIndex = $t02364123763._7 | |
721 | 777 | let finalizedEpoch = blockMeta(getStringValue(finalizedBlockKey))._2 | |
722 | 778 | let epochRef = if ((refEpoch >= finalizedEpoch)) | |
723 | 779 | then refEpoch | |
724 | - | else throw(((((" | |
780 | + | else throw((((("Can not start alt chain from epoch " + toString(refEpoch)) + ", epoch ") + toString(finalizedEpoch)) + " is finalized")) | |
725 | 781 | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, mainChainId, referenceHex) | |
726 | 782 | if ((checkChain == checkChain)) | |
727 | 783 | then { | |
728 | - | let newChainId = (valueOrElse(getInteger(lastChainIdKey), 0) + 1) | |
729 | - | let newChainHeight = (blockMeta(referenceHex)._1 + 1) | |
730 | - | let thisEpochMeta = match epochMeta(height) { | |
731 | - | case _: Unit => | |
732 | - | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(epochRef)) + SEP) + blockHashHex)) | |
733 | - | case other => | |
734 | - | throw("Epoch already started") | |
735 | - | } | |
736 | - | [thisEpochMeta, mkBlockMetaEntry(blockHashHex, newChainHeight, referenceHex, i.originCaller, newChainId, elToClTransfersRootHashHex), StringEntry(chainFirstBlockIdKey(newChainId), blockHashHex), StringEntry(chainMetaKey(newChainId), ((toString(newChainHeight) + SEP) + 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)] | |
784 | + | let checkTransfers = ensureCorrectTransfers(refTransferIndex, lastClToElTransferIndex, true) | |
785 | + | if ((checkTransfers == checkTransfers)) | |
786 | + | then { | |
787 | + | let newChainId = (valueOrElse(getInteger(lastChainIdKey), 0) + 1) | |
788 | + | let newChainHeight = (refChainHeight + 1) | |
789 | + | let thisEpochMeta = match epochMeta(height) { | |
790 | + | case _: Unit => | |
791 | + | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(epochRef)) + SEP) + blockHashHex)) | |
792 | + | case other => | |
793 | + | throw("Epoch already started") | |
794 | + | } | |
795 | + | [thisEpochMeta, mkBlockMetaEntry(blockHashHex, newChainHeight, referenceHex, i.originCaller, newChainId, elToClTransfersRootHashHex, lastClToElTransferIndex), 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)] | |
796 | + | } | |
797 | + | else throw("Strict value is not equal to itself.") | |
737 | 798 | } | |
738 | 799 | else throw("Strict value is not equal to itself.") | |
739 | 800 | } | |
749 | 810 | ||
750 | 811 | ||
751 | 812 | @Callable(i) | |
752 | - | func extendAltChain (chainId,blockHashHex,referenceHex,epoch) = { | |
753 | - | let | |
754 | - | if (( | |
813 | + | func extendAltChain_v2 (chainId,blockHashHex,referenceHex,epoch,elToClTransfersRootHashHex) = { | |
814 | + | let checkClToEl = requireClToElTransfersDisabled() | |
815 | + | if ((checkClToEl == checkClToEl)) | |
755 | 816 | then { | |
756 | 817 | let checkBlockHash = validateBlockHash(blockHashHex) | |
757 | 818 | if ((checkBlockHash == checkBlockHash)) | |
766 | 827 | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, chainId, toBase16String(chainFirstBlockMeta._3)) | |
767 | 828 | if ((checkChain == checkChain)) | |
768 | 829 | then { | |
769 | - | let $ | |
770 | - | let chainHeight = $ | |
771 | - | let chainLastBlock = $ | |
830 | + | let $t02613726191 = chainMeta(chainId) | |
831 | + | let chainHeight = $t02613726191._1 | |
832 | + | let chainLastBlock = $t02613726191._2 | |
772 | 833 | let checkReference = isReferenceCorrect(referenceHex, chainLastBlock) | |
773 | 834 | if ((checkReference == checkReference)) | |
774 | 835 | then { | |
792 | 853 | else false) | |
793 | 854 | then [IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), chainFirstBlockMeta._1)] | |
794 | 855 | else nil | |
795 | - | let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(referenceHex)) + i.originCaller.bytes) | |
796 | - | ((([BinaryEntry((blockMetaK + blockHashHex), newBlockMeta), StringEntry(chainMetaKey(chainId), ((toString(newChainHeight) + SEP) + blockHashHex)), thisEpochMeta, IntegerEntry(minerChainIdKey(i.originCaller), chainId), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight)] ++ updateMainChainData) ++ addSupporter(chainId, i.originCaller)) ++ updateMainChainLastMinedBlock) | |
856 | + | ((([mkBlockMetaEntry(blockHashHex, newChainHeight, referenceHex, i.originCaller, chainId, elToClTransfersRootHashHex, -1), mkChainMetaEntry(chainId, newChainHeight, blockHashHex), thisEpochMeta, IntegerEntry(minerChainIdKey(i.originCaller), chainId), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight)] ++ updateMainChainData) ++ addSupporter(chainId, i.originCaller)) ++ updateMainChainLastMinedBlock) | |
797 | 857 | } | |
798 | 858 | else throw("Strict value is not equal to itself.") | |
799 | 859 | } | |
811 | 871 | ||
812 | 872 | ||
813 | 873 | @Callable(i) | |
814 | - | func extendAltChain_ | |
815 | - | let | |
816 | - | if (( | |
874 | + | func extendAltChain_v3 (chainId,blockHashHex,referenceHex,epoch,elToClTransfersRootHashHex,lastClToElTransferIndex) = { | |
875 | + | let checkElToCl = requireClToElTransfersEnabled() | |
876 | + | if ((checkElToCl == checkElToCl)) | |
817 | 877 | then { | |
818 | 878 | let checkBlockHash = validateBlockHash(blockHashHex) | |
819 | 879 | if ((checkBlockHash == checkBlockHash)) | |
828 | 888 | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, chainId, toBase16String(chainFirstBlockMeta._3)) | |
829 | 889 | if ((checkChain == checkChain)) | |
830 | 890 | then { | |
831 | - | let $ | |
832 | - | let chainHeight = $ | |
833 | - | let chainLastBlock = $ | |
891 | + | let $t02873528789 = chainMeta(chainId) | |
892 | + | let chainHeight = $t02873528789._1 | |
893 | + | let chainLastBlock = $t02873528789._2 | |
834 | 894 | let checkReference = isReferenceCorrect(referenceHex, chainLastBlock) | |
835 | 895 | if ((checkReference == checkReference)) | |
836 | 896 | then { | |
837 | - | let newChainHeight = (chainHeight + 1) | |
838 | - | let prevEpoch = blockMeta(referenceHex)._2 | |
839 | - | let updateMainChainData = if ((supportingBalance(chainId) > (computedTotalBalance / 2))) | |
897 | + | let checkTransfers = ensureCorrectTransfers(blockMeta(referenceHex)._7, lastClToElTransferIndex, true) | |
898 | + | if ((checkTransfers == checkTransfers)) | |
840 | 899 | then { | |
841 | - | let lastChainId = valueOrElse(getInteger(lastChainIdKey), 0) | |
842 | - | let updateFinalizedBlock = getUpdateFinalizedBlockAction(i.originCaller, blockHashHex, prevEpoch) | |
843 | - | ([IntegerEntry(chainForkedHeightKey(mainChainId), chainFirstBlockMeta._1), IntegerEntry(mainChainIdKey, chainId), IntegerEntry(firstValidAltChainIdKey, (lastChainId + 1))] ++ updateFinalizedBlock) | |
900 | + | let newChainHeight = (chainHeight + 1) | |
901 | + | let prevEpoch = blockMeta(referenceHex)._2 | |
902 | + | let updateMainChainData = if ((supportingBalance(chainId) > (computedTotalBalance / 2))) | |
903 | + | then { | |
904 | + | let lastChainId = valueOrElse(getInteger(lastChainIdKey), 0) | |
905 | + | let updateFinalizedBlock = getUpdateFinalizedBlockAction(i.originCaller, blockHashHex, prevEpoch) | |
906 | + | ([IntegerEntry(chainForkedHeightKey(mainChainId), chainFirstBlockMeta._1), IntegerEntry(mainChainIdKey, chainId), IntegerEntry(firstValidAltChainIdKey, (lastChainId + 1))] ++ updateFinalizedBlock) | |
907 | + | } | |
908 | + | else nil | |
909 | + | let thisEpochMeta = match epochMeta(height) { | |
910 | + | case _: Unit => | |
911 | + | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(prevEpoch)) + SEP) + blockHashHex)) | |
912 | + | case other => | |
913 | + | throw("Epoch already started") | |
844 | 914 | } | |
845 | - | else nil | |
846 | - | let thisEpochMeta = match epochMeta(height) { | |
847 | - | case _: Unit => | |
848 | - | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(prevEpoch)) + SEP) + blockHashHex)) | |
849 | - | case other => | |
850 | - | throw("Epoch already started") | |
851 | - | } | |
852 | - | let updateMainChainLastMinedBlock = if (if ((updateMainChainData == nil)) | |
853 | - | then (valueOrElse(minerChainId(i.originCaller), 0) != chainId) | |
854 | - | else false) | |
855 | - | then [IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), chainFirstBlockMeta._1)] | |
856 | - | else nil | |
857 | - | ((([mkBlockMetaEntry(blockHashHex, newChainHeight, referenceHex, i.originCaller, chainId, elToClTransfersRootHashHex), StringEntry(chainMetaKey(chainId), ((toString(newChainHeight) + SEP) + blockHashHex)), thisEpochMeta, IntegerEntry(minerChainIdKey(i.originCaller), chainId), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight)] ++ updateMainChainData) ++ addSupporter(chainId, i.originCaller)) ++ updateMainChainLastMinedBlock) | |
915 | + | if ((thisEpochMeta == thisEpochMeta)) | |
916 | + | then { | |
917 | + | let updateMainChainLastMinedBlock = if (if ((updateMainChainData == nil)) | |
918 | + | then (valueOrElse(minerChainId(i.originCaller), 0) != chainId) | |
919 | + | else false) | |
920 | + | then [IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), chainFirstBlockMeta._1)] | |
921 | + | else nil | |
922 | + | ((([mkBlockMetaEntry(blockHashHex, newChainHeight, referenceHex, i.originCaller, chainId, elToClTransfersRootHashHex, lastClToElTransferIndex), mkChainMetaEntry(chainId, newChainHeight, blockHashHex), thisEpochMeta, IntegerEntry(minerChainIdKey(i.originCaller), chainId), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight)] ++ updateMainChainData) ++ addSupporter(chainId, i.originCaller)) ++ updateMainChainLastMinedBlock) | |
923 | + | } | |
924 | + | else throw("Strict value is not equal to itself.") | |
925 | + | } | |
926 | + | else throw("Strict value is not equal to itself.") | |
858 | 927 | } | |
859 | 928 | else throw("Strict value is not equal to itself.") | |
860 | 929 | } | |
873 | 942 | ||
874 | 943 | @Callable(i) | |
875 | 944 | func join (rewardAddress) = { | |
876 | - | func | |
945 | + | func ensureNotOverrideOtherMinerPk (elAddressHex) = match getBinary(minerPkKey(elAddressHex)) { | |
877 | 946 | case pk: ByteVector => | |
878 | 947 | if ((pk == i.originCallerPublicKey)) | |
879 | 948 | then unit | |
880 | - | else throw((((" | |
949 | + | else throw(((("EL miner address " + elAddressHex) + " is already linked with ") + toBase58String(pk))) | |
881 | 950 | case _ => | |
882 | 951 | unit | |
883 | 952 | } | |
914 | 983 | else { | |
915 | 984 | let newMiner = toString(i.originCaller) | |
916 | 985 | let rewardAddressHex = toBase16String(rewardAddress) | |
917 | - | let check = | |
986 | + | let check = ensureNotOverrideOtherMinerPk(rewardAddressHex) | |
918 | 987 | if ((check == check)) | |
919 | 988 | then { | |
920 | 989 | let newMiners = if ((size(allMiners) == 0)) | |
962 | 1031 | let prevRewardAddress = valueOrErrorMessage(getString(this, rewardAddrKey), "miner has never joined") | |
963 | 1032 | if ((thisEpochMiner == i.originCaller)) | |
964 | 1033 | then throw("designated miner can't leave") | |
965 | - | else [StringEntry(allMinersKey, makeString_2C(remainingMiners, SEP)), DeleteEntry(rewardAddrKey), DeleteEntry(minerPkKey(prevRewardAddress))] | |
1034 | + | else [StringEntry(allMinersKey, makeString_2C(remainingMiners, SEP))] | |
1035 | + | } | |
1036 | + | ||
1037 | + | ||
1038 | + | ||
1039 | + | @Callable(i) | |
1040 | + | func transfer (destElAddressHex) = { | |
1041 | + | let checkClToEl = requireClToElTransfersEnabled() | |
1042 | + | if ((checkClToEl == checkClToEl)) | |
1043 | + | then { | |
1044 | + | let checkPayment = if ((size(i.payments) == 1)) | |
1045 | + | then true | |
1046 | + | else throw(("Expected one payment as a transfer amount, got " + toString(size(i.payments)))) | |
1047 | + | if ((checkPayment == checkPayment)) | |
1048 | + | then { | |
1049 | + | let tokenIdStr = getStringValue(tokenIdKey) | |
1050 | + | let tokenId = fromBase58String(tokenIdStr) | |
1051 | + | let t = i.payments[0] | |
1052 | + | let checkAssetId = match t.assetId { | |
1053 | + | case assetId: ByteVector => | |
1054 | + | if ((assetId == tokenId)) | |
1055 | + | then true | |
1056 | + | else throw(((("Expected " + tokenIdStr) + " in the payment, got ") + toBase58String(assetId))) | |
1057 | + | case _ => | |
1058 | + | throw((("Expected " + tokenIdStr) + " in the payment, got Waves")) | |
1059 | + | } | |
1060 | + | if ((checkAssetId == checkAssetId)) | |
1061 | + | then { | |
1062 | + | let lastIndex = blockMeta(mainChainLastBlock)._7 | |
1063 | + | let queueSize = ((nativeTransfersCount - lastIndex) + 1) | |
1064 | + | let checkQueueLimit = if ((160 > queueSize)) | |
1065 | + | then amountGtEq(t, 1000000, queueSize) | |
1066 | + | else if ((1600 > queueSize)) | |
1067 | + | then amountGtEq(t, 10000000, queueSize) | |
1068 | + | else if ((3200 > queueSize)) | |
1069 | + | then amountGtEq(t, 100000000, queueSize) | |
1070 | + | else if ((6400 > queueSize)) | |
1071 | + | then amountGtEq(t, 1000000000, queueSize) | |
1072 | + | else throw((("Transfers denied for queue size of " + toString(queueSize)) + ". Wait until current transfers processed")) | |
1073 | + | if ((checkQueueLimit == checkQueueLimit)) | |
1074 | + | then [IntegerEntry(nativeTransfersCountKey, (nativeTransfersCount + 1)), mkNativeTransferEntry(nativeTransfersCount, destElAddressHex, t.amount), Burn(tokenId, t.amount)] | |
1075 | + | else throw("Strict value is not equal to itself.") | |
1076 | + | } | |
1077 | + | else throw("Strict value is not equal to itself.") | |
1078 | + | } | |
1079 | + | else throw("Strict value is not equal to itself.") | |
1080 | + | } | |
1081 | + | else throw("Strict value is not equal to itself.") | |
966 | 1082 | } | |
967 | 1083 | ||
968 | 1084 | ||
969 | 1085 | ||
970 | 1086 | @Callable(i) | |
971 | 1087 | func withdraw (blockHashHex,merkleProof,transferIndexInBlock,amount) = { | |
972 | - | let checkActivation = requireElToClTransfersEnabled() | |
973 | - | if ((checkActivation == checkActivation)) | |
974 | - | then { | |
975 | - | let withdrawBlockMeta = blockMeta(blockHashHex) | |
976 | - | let withdrawBlockHeight = withdrawBlockMeta._1 | |
977 | - | let finalizedBlockHeight = blockMeta(getStringValue(finalizedBlockKey))._1 | |
978 | - | let mainChainLastBlockHeight = blockMeta(mainChainLastBlock)._1 | |
979 | - | if ((withdrawBlockHeight > finalizedBlockHeight)) | |
980 | - | then throw(((("EL block #" + toString(withdrawBlockHeight)) + " is not finalized. The current finalized is #") + toString(finalizedBlockHeight))) | |
981 | - | else { | |
982 | - | let withdrawBlockChainId = withdrawBlockMeta._5 | |
983 | - | let isMainChain = (withdrawBlockChainId == mainChainId) | |
984 | - | let relatesToMainChain = match getInteger(chainForkedHeightKey(withdrawBlockChainId)) { | |
985 | - | case forkedHeight: Int => | |
986 | - | (forkedHeight > withdrawBlockHeight) | |
987 | - | case _ => | |
988 | - | throw((((blockHashHex + " is on an alternative chain #") + toString(withdrawBlockChainId)) + " that was not approved by majority. Wait for some blocks")) | |
1088 | + | let withdrawBlockMeta = blockMeta(blockHashHex) | |
1089 | + | let withdrawBlockHeight = withdrawBlockMeta._1 | |
1090 | + | let finalizedBlockHeight = blockMeta(getStringValue(finalizedBlockKey))._1 | |
1091 | + | let mainChainLastBlockHeight = blockMeta(mainChainLastBlock)._1 | |
1092 | + | if ((withdrawBlockHeight > finalizedBlockHeight)) | |
1093 | + | then throw(((("EL block #" + toString(withdrawBlockHeight)) + " is not finalized. The current finalized is #") + toString(finalizedBlockHeight))) | |
1094 | + | else { | |
1095 | + | let withdrawBlockChainId = withdrawBlockMeta._5 | |
1096 | + | let isMainChain = (withdrawBlockChainId == mainChainId) | |
1097 | + | let relatesToMainChain = match getInteger(chainForkedHeightKey(withdrawBlockChainId)) { | |
1098 | + | case forkedHeight: Int => | |
1099 | + | (forkedHeight > withdrawBlockHeight) | |
1100 | + | case _ => | |
1101 | + | throw((((blockHashHex + " is on an alternative chain #") + toString(withdrawBlockChainId)) + " that was not approved by majority. Wait for some blocks")) | |
1102 | + | } | |
1103 | + | if (if (isMainChain) | |
1104 | + | then true | |
1105 | + | else relatesToMainChain) | |
1106 | + | then { | |
1107 | + | let recipient = i.originCaller | |
1108 | + | let recipientPkHash = take(drop(recipient.bytes, 2), PUBLIC_KEY_HASH_SIZE) | |
1109 | + | let zeroAmountBytes = base58'11111111111111111111111111111111111111111111' | |
1110 | + | let amountBytes = toBytes(amount) | |
1111 | + | let elEventData = ((recipientPkHash + take(zeroAmountBytes, (size(zeroAmountBytes) - size(amountBytes)))) + amountBytes) | |
1112 | + | let elEventDataDigest = blake2b256_16Kb(elEventData) | |
1113 | + | let calculatedRootHash = createMerkleRoot(merkleProof, elEventDataDigest, transferIndexInBlock) | |
1114 | + | let expectedRootHash = withdrawBlockMeta._6 | |
1115 | + | if ((calculatedRootHash == expectedRootHash)) | |
1116 | + | then { | |
1117 | + | let tokenId = fromBase58String(getStringValue(tokenIdKey)) | |
1118 | + | let transfersKey = blockElToClTransfersKey(blockHashHex) | |
1119 | + | [Reissue(tokenId, amount, true), ScriptTransfer(recipient, amount, tokenId), StringEntry(transfersKey, setOrFail(valueOrElse(getString(transfersKey), ""), transferIndexInBlock))] | |
1120 | + | } | |
1121 | + | else throw((((((("Expected root hash: " + toBase16String(expectedRootHash)) + ", got: ") + toBase16String(calculatedRootHash)) + ". Event data digest: ") + toBase64String(elEventDataDigest)) + ". Check your withdraw arguments")) | |
989 | 1122 | } | |
990 | - | if (if (isMainChain) | |
991 | - | then true | |
992 | - | else relatesToMainChain) | |
993 | - | then { | |
994 | - | let recipient = i.originCaller | |
995 | - | let recipientPkHash = take(drop(recipient.bytes, 2), PUBLIC_KEY_HASH_SIZE) | |
996 | - | let zeroAmountBytes = base58'11111111111111111111111111111111111111111111' | |
997 | - | let amountBytes = toBytes(amount) | |
998 | - | let elEventData = ((recipientPkHash + take(zeroAmountBytes, (size(zeroAmountBytes) - size(amountBytes)))) + amountBytes) | |
999 | - | let elEventDataDigest = blake2b256_16Kb(elEventData) | |
1000 | - | let calculatedRootHash = createMerkleRoot(merkleProof, elEventDataDigest, transferIndexInBlock) | |
1001 | - | let expectedRootHash = withdrawBlockMeta._6 | |
1002 | - | if ((calculatedRootHash == expectedRootHash)) | |
1003 | - | then { | |
1004 | - | let tokenId = fromBase58String(getStringValue(tokenIdKey)) | |
1005 | - | let transfersKey = blockElToClTransfersKey(blockHashHex) | |
1006 | - | [Reissue(tokenId, amount, true), ScriptTransfer(recipient, amount, tokenId), StringEntry(transfersKey, setOrFail(valueOrElse(getString(transfersKey), ""), transferIndexInBlock))] | |
1007 | - | } | |
1008 | - | else throw(((((("Expected root hash: " + toBase16String(expectedRootHash)) + ", got: ") + toBase16String(calculatedRootHash)) + ". Event data digest: ") + toBase64String(elEventDataDigest))) | |
1009 | - | } | |
1010 | - | else throw((("Expected " + blockHashHex) + " to be either on the main chain or relate to it")) | |
1011 | - | } | |
1123 | + | else throw((("Expected " + blockHashHex) + " to be either on the main chain or relate to it")) | |
1012 | 1124 | } | |
1013 | - | else throw("Strict value is not equal to itself.") | |
1014 | 1125 | } | |
1015 | 1126 | ||
1016 | 1127 | ||
1028 | 1139 | let genesisBlockReferenceHash = "0000000000000000000000000000000000000000000000000000000000000000" | |
1029 | 1140 | let issue = Issue("UNIT0", "Native token", 0, 8, true) | |
1030 | 1141 | let tokenId = calculateAssetId(issue) | |
1031 | - | [mkBlockMetaEntry(genesisBlockHashHex, 0, genesisBlockReferenceHash, genesisMinerAddress, mainChainId, ""), StringEntry(chainFirstBlockIdKey(0), genesisBlockHashHex), StringEntry(chainMetaKey(0), ("0," + genesisBlockHashHex)), IntegerEntry(minerRewardKey, minerRewardInGwei), StringEntry(stakingContractAddressKey, stakingContractAddressB58), StringEntry(epochMetaKey(height), ((toString(genesisMinerAddress) + ",0,") + genesisBlockHashHex)), StringEntry(finalizedBlockKey, genesisBlockHashHex), IntegerEntry(prevRandaoHeightKey, 0), IntegerEntry(elToClTransfersEpochKey, 0), issue, StringEntry(tokenIdKey, toBase58String(tokenId)), StringEntry(elBridgeAddressKey, ("0x" + elBridgeAddressHex))] | |
1142 | + | let genesisBlockMeta = ((((((toBytes(0) + toBytes(height)) + fromBase16String(genesisBlockReferenceHash)) + genesisMinerAddress.bytes) + toBytes(0)) + base58'') + toBytes(-1)) | |
1143 | + | [BinaryEntry((blockMetaK + genesisBlockHashHex), genesisBlockMeta), StringEntry(chainFirstBlockIdKey(0), genesisBlockHashHex), mkChainMetaEntry(0, 0, genesisBlockHashHex), IntegerEntry(minerRewardKey, minerRewardInGwei), StringEntry(stakingContractAddressKey, stakingContractAddressB58), StringEntry(epochMetaKey(height), ((toString(genesisMinerAddress) + ",0,") + genesisBlockHashHex)), StringEntry(finalizedBlockKey, genesisBlockHashHex), issue, StringEntry(tokenIdKey, toBase58String(tokenId)), StringEntry(elBridgeAddressKey, ("0x" + elBridgeAddressHex)), IntegerEntry(clToElTransfersEpochKey, 0)] | |
1032 | 1144 | } | |
1033 | - | ||
1034 | - | ||
1035 | - | ||
1036 | - | @Callable(i) | |
1037 | - | func setupElToClTransfers (activationHeight,elBridgeAddressHex) = if (isDefined(getInteger(elToClTransfersEpochKey))) | |
1038 | - | then throw("EL to CL transfers feature was set up") | |
1039 | - | else { | |
1040 | - | let issue = Issue("UNIT0", "Native token", 0, 8, true) | |
1041 | - | let tokenId = calculateAssetId(issue) | |
1042 | - | [IntegerEntry(elToClTransfersEpochKey, activationHeight), issue, StringEntry(tokenIdKey, toBase58String(tokenId)), StringEntry(elBridgeAddressKey, ("0x" + elBridgeAddressHex))] | |
1043 | - | } | |
1044 | 1145 | ||
1045 | 1146 |
Old | New | Differences | |
---|---|---|---|
1 | 1 | {-# STDLIB_VERSION 8 #-} | |
2 | 2 | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | 3 | {-# CONTENT_TYPE DAPP #-} | |
4 | 4 | let INT_MAX = 9223372036854775807 | |
5 | 5 | ||
6 | 6 | let WAVES = 100000000 | |
7 | 7 | ||
8 | 8 | let MIN_BALANCE = (20000 * WAVES) | |
9 | 9 | ||
10 | 10 | let SEP = "," | |
11 | 11 | ||
12 | 12 | let BLOCK_HASH_SIZE = 32 | |
13 | 13 | ||
14 | 14 | let ADDRESS_SIZE = 26 | |
15 | 15 | ||
16 | 16 | let PUBLIC_KEY_HASH_SIZE = 20 | |
17 | + | ||
18 | + | let ROOT_HASH_SIZE = 32 | |
19 | + | ||
20 | + | let MAX_CL_TO_EL_TRANSFERS = 16 | |
17 | 21 | ||
18 | 22 | let zeroesStr = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" | |
19 | 23 | ||
20 | 24 | let thisEpochDataKey = "thisEpochData" | |
21 | 25 | ||
22 | 26 | let allMinersKey = "allMiners" | |
23 | 27 | ||
24 | 28 | let mainChainIdKey = "mainChainId" | |
25 | 29 | ||
26 | 30 | let lastChainIdKey = "lastChainId" | |
27 | 31 | ||
28 | 32 | let firstValidAltChainIdKey = "firstValidAltChainId" | |
29 | 33 | ||
30 | 34 | let minerRewardKey = "minerReward" | |
31 | 35 | ||
32 | 36 | let stakingContractAddressKey = "stakingContractAddress" | |
33 | 37 | ||
34 | 38 | let blockMetaK = "block_0x" | |
35 | 39 | ||
36 | 40 | let finalizedBlockKey = "finalizedBlock" | |
37 | 41 | ||
38 | 42 | let tokenIdKey = "tokenId" | |
39 | 43 | ||
40 | 44 | let elBridgeAddressKey = "elBridgeAddress" | |
41 | 45 | ||
42 | - | let | |
46 | + | let nativeTransfersCountKey = "nativeTransfersCount" | |
43 | 47 | ||
44 | - | let | |
48 | + | let clToElTransfersEpochKey = "clToElTransfersEpoch" | |
45 | 49 | ||
46 | 50 | func pad (i) = { | |
47 | 51 | let s = toString(i) | |
48 | 52 | match size(s) { | |
49 | 53 | case _ => | |
50 | 54 | if ((1 == $match0)) | |
51 | 55 | then ("0000000" + s) | |
52 | 56 | else if ((2 == $match0)) | |
53 | 57 | then ("000000" + s) | |
54 | 58 | else if ((3 == $match0)) | |
55 | 59 | then ("00000" + s) | |
56 | 60 | else if ((4 == $match0)) | |
57 | 61 | then ("0000" + s) | |
58 | 62 | else if ((5 == $match0)) | |
59 | 63 | then ("000" + s) | |
60 | 64 | else if ((6 == $match0)) | |
61 | 65 | then ("00" + s) | |
62 | 66 | else if ((7 == $match0)) | |
63 | 67 | then ("0" + s) | |
64 | 68 | else s | |
65 | 69 | } | |
66 | 70 | } | |
67 | 71 | ||
68 | 72 | ||
69 | 73 | func blockElToClTransfersKey (blockHashHex) = ("elToClTransfers_0x" + blockHashHex) | |
70 | 74 | ||
71 | 75 | ||
72 | 76 | func epochMetaKey (epoch) = ("epoch_" + pad(epoch)) | |
73 | 77 | ||
74 | 78 | ||
75 | 79 | func chainFirstBlockIdKey (chainId) = (("chain" + toString(chainId)) + "FirstBlock") | |
76 | 80 | ||
77 | 81 | ||
78 | 82 | func chainMetaKey (chainId) = ("chain_" + pad(chainId)) | |
79 | 83 | ||
80 | 84 | ||
81 | 85 | func chainLastHeightKey (chainId,miner) = ((("chain_" + pad(chainId)) + "_") + toString(miner)) | |
82 | 86 | ||
83 | 87 | ||
84 | 88 | func chainForkedHeightKey (chainId) = (("chain_" + pad(chainId)) + "ForkedHeight") | |
85 | 89 | ||
86 | 90 | ||
87 | 91 | func supportersKey (chainId) = (("chain" + toString(chainId)) + "Supporters") | |
88 | 92 | ||
89 | 93 | ||
90 | 94 | func minerRewardAddressKey (minerAddr) = (("miner_" + minerAddr) + "_RewardAddress") | |
91 | 95 | ||
92 | 96 | ||
93 | 97 | func minerPkKey (rewardAddress) = (("miner_0x" + rewardAddress) + "_PK") | |
94 | 98 | ||
95 | 99 | ||
96 | 100 | func minerChainIdKey (miner) = (("miner_" + toString(miner)) + "_ChainId") | |
97 | 101 | ||
98 | 102 | ||
99 | - | let | |
103 | + | let nativeTransfersCount = valueOrElse(getInteger(this, nativeTransfersCountKey), 0) | |
100 | 104 | ||
101 | - | func requireElToClTransfersDisabled () = if ((elToClTransfersEpoch > height)) | |
102 | - | then unit | |
103 | - | else throw((("EL to CL transfers activated on epoch #" + toString(height)) + ". Upgrade your client")) | |
105 | + | func nativeTransferKey (index) = ("nativeTransfer_" + toString(index)) | |
104 | 106 | ||
105 | 107 | ||
106 | - | func requireElToClTransfersEnabled () = if ((height >= elToClTransfersEpoch)) | |
108 | + | func mkNativeTransferEntry (index,destElAddressHex,amount) = StringEntry(nativeTransferKey(index), ((("0x" + destElAddressHex) + SEP) + toString(amount))) | |
109 | + | ||
110 | + | ||
111 | + | let clToElTransfersEpoch = valueOrElse(getInteger(this, clToElTransfersEpochKey), INT_MAX) | |
112 | + | ||
113 | + | func requireClToElTransfersDisabled () = if ((clToElTransfersEpoch > height)) | |
107 | 114 | then unit | |
108 | - | else throw(((("EL to CL transfers haven't activated on epoch #" + toString(height)) + ". Wait for activation on #") + toString(elToClTransfersEpoch))) | |
115 | + | else throw((("CL to EL transfers activated on epoch #" + toString(height)) + ". Upgrade your client")) | |
116 | + | ||
117 | + | ||
118 | + | func requireClToElTransfersEnabled () = if ((height >= clToElTransfersEpoch)) | |
119 | + | then unit | |
120 | + | else throw(((("CL to EL transfers haven't activated on epoch #" + toString(height)) + ". Wait for activation on #") + toString(clToElTransfersEpoch))) | |
121 | + | ||
122 | + | ||
123 | + | func ensureCorrectTransfers (refTransferIndex,transferIndex,expectReward) = { | |
124 | + | let maxTransfers = if (expectReward) | |
125 | + | then (MAX_CL_TO_EL_TRANSFERS - 1) | |
126 | + | else MAX_CL_TO_EL_TRANSFERS | |
127 | + | let actualTransfers = (transferIndex - refTransferIndex) | |
128 | + | let checkNumber = if ((actualTransfers > maxTransfers)) | |
129 | + | then throw(((("Allowed only " + toString(maxTransfers)) + " transfers, got ") + toString(actualTransfers))) | |
130 | + | else true | |
131 | + | if ((checkNumber == checkNumber)) | |
132 | + | then if ((transferIndex >= nativeTransfersCount)) | |
133 | + | then throw(((("Attempt to transfer #" + toString(transferIndex)) + ". Available transfers: ") + toString(nativeTransfersCount))) | |
134 | + | else true | |
135 | + | else throw("Strict value is not equal to itself.") | |
136 | + | } | |
137 | + | ||
138 | + | ||
139 | + | func amountGtEq (t,gtEq,queueSize) = if ((t.amount >= gtEq)) | |
140 | + | then true | |
141 | + | else throw((((((("Transferring amount " + toString(t.amount)) + " should be >= ") + toString(gtEq)) + " for queue size of ") + toString(queueSize)) + ". Transfer more or wait")) | |
109 | 142 | ||
110 | 143 | ||
111 | 144 | let stakingContractAddress = match getString(this, stakingContractAddressKey) { | |
112 | 145 | case s: String => | |
113 | 146 | valueOrErrorMessage(addressFromString(s), ("invalid staking contract address: " + s)) | |
114 | 147 | case _ => | |
115 | 148 | Address(getBinaryValue(this, stakingContractAddressKey)) | |
116 | 149 | } | |
117 | 150 | ||
118 | 151 | func generatingBalance (address) = match getString(stakingContractAddress, ("%s__" + toString(address))) { | |
119 | 152 | case str: String => | |
120 | 153 | let paramList = split(str, "__") | |
121 | 154 | let prevHeight = parseIntValue(paramList[1]) | |
122 | 155 | let prevBalance = parseIntValue(paramList[2]) | |
123 | 156 | let nextHeight = parseIntValue(paramList[3]) | |
124 | 157 | let nextBalance = parseIntValue(paramList[4]) | |
125 | 158 | if ((height >= nextHeight)) | |
126 | 159 | then nextBalance | |
127 | 160 | else if ((height >= prevHeight)) | |
128 | 161 | then prevBalance | |
129 | 162 | else 0 | |
130 | 163 | case _ => | |
131 | 164 | 0 | |
132 | 165 | } | |
133 | 166 | ||
134 | 167 | ||
135 | 168 | func chainMeta (chainId) = { | |
136 | 169 | let s = getStringValue(chainMetaKey(chainId)) | |
137 | 170 | let items = split(s, SEP) | |
138 | 171 | $Tuple2(parseIntValue(items[0]), items[1]) | |
139 | 172 | } | |
140 | 173 | ||
141 | 174 | ||
175 | + | func mkChainMetaEntry (chainId,newChainHeight,blockHashHex) = StringEntry(chainMetaKey(chainId), ((toString(newChainHeight) + SEP) + blockHashHex)) | |
176 | + | ||
177 | + | ||
142 | 178 | let mainChainId = valueOrElse(getInteger(mainChainIdKey), 0) | |
143 | 179 | ||
144 | - | let $ | |
180 | + | let $t064236489 = chainMeta(mainChainId) | |
145 | 181 | ||
146 | - | let mainChainHeight = $ | |
182 | + | let mainChainHeight = $t064236489._1 | |
147 | 183 | ||
148 | - | let mainChainLastBlock = $ | |
184 | + | let mainChainLastBlock = $t064236489._2 | |
149 | 185 | ||
150 | 186 | func epochMeta (epoch) = match getString(epochMetaKey(epoch)) { | |
151 | 187 | case s: String => | |
152 | 188 | let fragments = split(s, SEP) | |
153 | 189 | $Tuple3(addressFromStringValue(fragments[0]), parseIntValue(fragments[1]), fragments[2]) | |
154 | 190 | case _ => | |
155 | 191 | unit | |
156 | 192 | } | |
157 | 193 | ||
158 | 194 | ||
159 | - | let $ | |
195 | + | let $t067207184 = match epochMeta(height) { | |
160 | 196 | case m: (Address, Int, String) => | |
161 | 197 | m | |
162 | 198 | case _ => | |
163 | 199 | match getString(thisEpochDataKey) { | |
164 | 200 | case rawThisEpochData: String => | |
165 | 201 | let thisEpochData = split(rawThisEpochData, SEP) | |
166 | 202 | let thisEpoch = parseIntValue(thisEpochData[0]) | |
167 | 203 | $Tuple3(if ((thisEpoch == height)) | |
168 | 204 | then addressFromStringValue(thisEpochData[1]) | |
169 | 205 | else unit, 0, "") | |
170 | 206 | case _ => | |
171 | 207 | $Tuple3(unit, 0, "") | |
172 | 208 | } | |
173 | 209 | } | |
174 | 210 | ||
175 | - | let thisEpochMiner = $ | |
211 | + | let thisEpochMiner = $t067207184._1 | |
176 | 212 | ||
177 | - | let thisEpochRef = $ | |
213 | + | let thisEpochRef = $t067207184._2 | |
178 | 214 | ||
179 | - | let thisEpochLastBlock = $ | |
215 | + | let thisEpochLastBlock = $t067207184._3 | |
180 | 216 | ||
181 | 217 | let allMinersStr = valueOrElse(getString(allMinersKey), "") | |
182 | 218 | ||
183 | 219 | let allMiners = match allMinersStr { | |
184 | 220 | case _ => | |
185 | 221 | if (("" == $match0)) | |
186 | 222 | then nil | |
187 | 223 | else if ($isInstanceOf($match0, "String")) | |
188 | 224 | then { | |
189 | 225 | let raw = $match0 | |
190 | 226 | split_4C(raw, SEP) | |
191 | 227 | } | |
192 | 228 | else throw("Match error") | |
193 | 229 | } | |
194 | 230 | ||
195 | 231 | func blockMeta (blockId) = { | |
196 | 232 | let meta = getBinaryValue((blockMetaK + blockId)) | |
233 | + | let metaSize = size(meta) | |
197 | 234 | let blockHeight = toInt(meta) | |
198 | 235 | let blockEpoch = toInt(meta, 8) | |
199 | 236 | let blockParent = take(drop(meta, 16), BLOCK_HASH_SIZE) | |
200 | 237 | let blockGenerator = take(drop(meta, (16 + BLOCK_HASH_SIZE)), ADDRESS_SIZE) | |
201 | - | if ((elToClTransfersEpoch > blockEpoch)) | |
202 | - | then $Tuple6(blockHeight, blockEpoch, blockParent, blockGenerator, 0, base58'') | |
203 | - | else { | |
204 | - | let chainId = toInt(drop(meta, ((16 + BLOCK_HASH_SIZE) + ADDRESS_SIZE))) | |
205 | - | let elToClTransfersRootHash = drop(meta, ((24 + BLOCK_HASH_SIZE) + ADDRESS_SIZE)) | |
206 | - | $Tuple6(blockHeight, blockEpoch, blockParent, blockGenerator, chainId, elToClTransfersRootHash) | |
207 | - | } | |
238 | + | let chainId = toInt(meta, ((16 + BLOCK_HASH_SIZE) + ADDRESS_SIZE)) | |
239 | + | let baseOffset = ((24 + BLOCK_HASH_SIZE) + ADDRESS_SIZE) | |
240 | + | let remainingBytes = (metaSize - baseOffset) | |
241 | + | let elToClTransfersRootHash = if ((remainingBytes >= ROOT_HASH_SIZE)) | |
242 | + | then take(drop(meta, baseOffset), ROOT_HASH_SIZE) | |
243 | + | else base58'' | |
244 | + | let lastClToElTransferIndex = if (if ((remainingBytes == 8)) | |
245 | + | then true | |
246 | + | else (remainingBytes > ROOT_HASH_SIZE)) | |
247 | + | then toInt(meta, (baseOffset + size(elToClTransfersRootHash))) | |
248 | + | else -1 | |
249 | + | $Tuple7(blockHeight, blockEpoch, blockParent, blockGenerator, chainId, elToClTransfersRootHash, lastClToElTransferIndex) | |
208 | 250 | } | |
209 | 251 | ||
210 | 252 | ||
211 | - | func mkBlockMetaEntry (blockHashHex,blockHeight,blockParentHex,blockGenerator,chainId,elToClTransfersRootHashHex) = { | |
253 | + | func mkBlockMetaEntry (blockHashHex,blockHeight,blockParentHex,blockGenerator,chainId,elToClTransfersRootHashHex,lastClToElTransferIndex) = { | |
212 | 254 | let blockEpochBytes = toBytes(height) | |
213 | - | let blockMetaBytes = (((((toBytes(blockHeight) + blockEpochBytes) + fromBase16String(blockParentHex)) + blockGenerator.bytes) + toBytes(chainId)) + fromBase16String(elToClTransfersRootHashHex)) | |
214 | - | BinaryEntry((blockMetaK + blockHashHex), blockMetaBytes) | |
255 | + | let blockMetaBytes1 = (((((toBytes(blockHeight) + blockEpochBytes) + fromBase16String(blockParentHex)) + blockGenerator.bytes) + toBytes(chainId)) + fromBase16String(elToClTransfersRootHashHex)) | |
256 | + | let blockMetaBytes2 = if ((clToElTransfersEpoch > height)) | |
257 | + | then base58'' | |
258 | + | else toBytes(lastClToElTransferIndex) | |
259 | + | BinaryEntry((blockMetaK + blockHashHex), (blockMetaBytes1 + blockMetaBytes2)) | |
215 | 260 | } | |
216 | 261 | ||
217 | 262 | ||
218 | 263 | func lastHeightBy (miner,chainId) = match getInteger(chainLastHeightKey(chainId, miner)) { | |
219 | 264 | case h: Int => | |
220 | 265 | h | |
221 | 266 | case _ => | |
222 | 267 | let blockHash = getStringValue(((("chain" + toString(chainId)) + "LastMinedBy") + toString(miner))) | |
223 | 268 | blockMeta(blockHash)._1 | |
224 | 269 | } | |
225 | 270 | ||
226 | 271 | ||
227 | - | let $ | |
272 | + | let $t0924610212 = { | |
228 | 273 | let hitSource = match lastBlock.vrf { | |
229 | 274 | case vrf: ByteVector => | |
230 | 275 | vrf | |
231 | 276 | case _ => | |
232 | 277 | lastBlock.generationSignature | |
233 | 278 | } | |
234 | 279 | func processMiner (prev,miner) = { | |
235 | - | let $ | |
236 | - | let prevDelay = $ | |
237 | - | let prevMiner = $ | |
238 | - | let prevTotalBalance = $ | |
239 | - | let prevMiners = $ | |
280 | + | let $t095449607 = prev | |
281 | + | let prevDelay = $t095449607._1 | |
282 | + | let prevMiner = $t095449607._2 | |
283 | + | let prevTotalBalance = $t095449607._3 | |
284 | + | let prevMiners = $t095449607._4 | |
240 | 285 | let minerAddress = addressFromStringValue(miner) | |
241 | 286 | let wavesGenBalance = wavesBalance(minerAddress).generating | |
242 | 287 | let minerBalance = generatingBalance(minerAddress) | |
243 | 288 | if (if ((MIN_BALANCE > wavesGenBalance)) | |
244 | 289 | then true | |
245 | 290 | else (0 >= minerBalance)) | |
246 | 291 | then prev | |
247 | 292 | else { | |
248 | 293 | let nextDelay = calculateDelay(minerAddress, minerBalance) | |
249 | 294 | if ((prevDelay > nextDelay)) | |
250 | 295 | then $Tuple4(nextDelay, miner, (prevTotalBalance + minerBalance), (prevMiners :+ miner)) | |
251 | 296 | else $Tuple4(prevDelay, prevMiner, (prevTotalBalance + minerBalance), (prevMiners :+ miner)) | |
252 | 297 | } | |
253 | 298 | } | |
254 | 299 | ||
255 | 300 | let $l = allMiners | |
256 | 301 | let $s = size($l) | |
257 | 302 | let $acc0 = $Tuple4(INT_MAX, "", 0, nil) | |
258 | 303 | func $f0_1 ($a,$i) = if (($i >= $s)) | |
259 | 304 | then $a | |
260 | 305 | else processMiner($a, $l[$i]) | |
261 | 306 | ||
262 | 307 | func $f0_2 ($a,$i) = if (($i >= $s)) | |
263 | 308 | then $a | |
264 | 309 | else throw("List size exceeds 50") | |
265 | 310 | ||
266 | 311 | $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) | |
267 | 312 | } | |
268 | 313 | ||
269 | - | let computedDelay = $ | |
314 | + | let computedDelay = $t0924610212._1 | |
270 | 315 | ||
271 | - | let computedGenerator = $ | |
316 | + | let computedGenerator = $t0924610212._2 | |
272 | 317 | ||
273 | - | let computedTotalBalance = $ | |
318 | + | let computedTotalBalance = $t0924610212._3 | |
274 | 319 | ||
275 | - | let filteredMiners = $ | |
320 | + | let filteredMiners = $t0924610212._4 | |
276 | 321 | ||
277 | 322 | func getChainLastBlockId (chainId) = chainMeta(chainId)._2 | |
278 | 323 | ||
279 | 324 | ||
280 | - | let $ | |
325 | + | let $t01027810385 = blockMeta(mainChainLastBlock) | |
281 | 326 | ||
282 | - | let mclbIgnored1 = $ | |
327 | + | let mclbIgnored1 = $t01027810385._1 | |
283 | 328 | ||
284 | - | let mainChainEpoch = $ | |
329 | + | let mainChainEpoch = $t01027810385._2 | |
285 | 330 | ||
286 | - | let mainChainParentHash = $ | |
331 | + | let mainChainParentHash = $t01027810385._3 | |
287 | 332 | ||
288 | - | let mainChainGenerator = $t083528487._4 | |
289 | - | ||
290 | - | let mclbIgnored2 = $t083528487._5 | |
291 | - | ||
292 | - | let mclbIgnored3 = $t083528487._6 | |
333 | + | let mainChainGenerator = $t01027810385._4 | |
293 | 334 | ||
294 | 335 | func calculateFinalizedBlockHash (curMiner,curPrevEpoch,curLastBlockHash) = { | |
295 | 336 | let offsets_100 = split_4C("::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::", "") | |
296 | 337 | let halfBalance = (computedTotalBalance / 2) | |
297 | 338 | func step (prev,next) = { | |
298 | - | let $ | |
299 | - | let thisEpoch = $ | |
300 | - | let totalBalance = $ | |
301 | - | let maybeSafeEpoch = $ | |
302 | - | let prevMiners = $ | |
339 | + | let $t01075110815 = prev | |
340 | + | let thisEpoch = $t01075110815._1 | |
341 | + | let totalBalance = $t01075110815._2 | |
342 | + | let maybeSafeEpoch = $t01075110815._3 | |
343 | + | let prevMiners = $t01075110815._4 | |
303 | 344 | match maybeSafeEpoch { | |
304 | 345 | case _: Unit => | |
305 | - | let $ | |
346 | + | let $t01087311039 = if ((thisEpoch == height)) | |
306 | 347 | then $Tuple3(curMiner, curPrevEpoch, curLastBlockHash) | |
307 | 348 | else value(epochMeta(thisEpoch)) | |
308 | - | let miner = $ | |
309 | - | let prevEpoch = $ | |
310 | - | let lastBlockHash = $ | |
349 | + | let miner = $t01087311039._1 | |
350 | + | let prevEpoch = $t01087311039._2 | |
351 | + | let lastBlockHash = $t01087311039._3 | |
311 | 352 | if ((prevEpoch == 0)) | |
312 | 353 | then $Tuple4(thisEpoch, totalBalance, lastBlockHash, allMiners) | |
313 | 354 | else { | |
314 | - | let $ | |
355 | + | let $t01116411366 = if (containsElement(prevMiners, miner)) | |
315 | 356 | then $Tuple2(totalBalance, prevMiners) | |
316 | 357 | else $Tuple2((totalBalance + generatingBalance(miner)), miner :: prevMiners) | |
317 | - | let newTotalBalance = $ | |
318 | - | let newMiners = $ | |
358 | + | let newTotalBalance = $t01116411366._1 | |
359 | + | let newMiners = $t01116411366._2 | |
319 | 360 | if ((newTotalBalance > halfBalance)) | |
320 | 361 | then $Tuple4(thisEpoch, newTotalBalance, lastBlockHash, allMiners) | |
321 | 362 | else $Tuple4(prevEpoch, newTotalBalance, unit, newMiners) | |
322 | 363 | } | |
323 | 364 | case _ => | |
324 | 365 | prev | |
325 | 366 | } | |
326 | 367 | } | |
327 | 368 | ||
328 | - | let $ | |
369 | + | let $t01159911732 = { | |
329 | 370 | let $l = (offsets_100 ++ offsets_100) | |
330 | 371 | let $s = size($l) | |
331 | 372 | let $acc0 = $Tuple4(height, 0, unit, nil) | |
332 | 373 | func $f0_1 ($a,$i) = if (($i >= $s)) | |
333 | 374 | then $a | |
334 | 375 | else step($a, $l[$i]) | |
335 | 376 | ||
336 | 377 | func $f0_2 ($a,$i) = if (($i >= $s)) | |
337 | 378 | then $a | |
338 | 379 | else throw("List size exceeds 200") | |
339 | 380 | ||
340 | 381 | $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($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($f0_1($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), 101), 102), 103), 104), 105), 106), 107), 108), 109), 110), 111), 112), 113), 114), 115), 116), 117), 118), 119), 120), 121), 122), 123), 124), 125), 126), 127), 128), 129), 130), 131), 132), 133), 134), 135), 136), 137), 138), 139), 140), 141), 142), 143), 144), 145), 146), 147), 148), 149), 150), 151), 152), 153), 154), 155), 156), 157), 158), 159), 160), 161), 162), 163), 164), 165), 166), 167), 168), 169), 170), 171), 172), 173), 174), 175), 176), 177), 178), 179), 180), 181), 182), 183), 184), 185), 186), 187), 188), 189), 190), 191), 192), 193), 194), 195), 196), 197), 198), 199), 200) | |
341 | 382 | } | |
342 | - | let fallbackEpoch = $ | |
343 | - | let totalBalance = $ | |
344 | - | let finalizedBlockHashOpt = $ | |
345 | - | let miners = $ | |
383 | + | let fallbackEpoch = $t01159911732._1 | |
384 | + | let totalBalance = $t01159911732._2 | |
385 | + | let finalizedBlockHashOpt = $t01159911732._3 | |
386 | + | let miners = $t01159911732._4 | |
346 | 387 | match finalizedBlockHashOpt { | |
347 | 388 | case finalizedBlockHash: String => | |
348 | 389 | finalizedBlockHash | |
349 | 390 | case _ => | |
350 | 391 | value(epochMeta(fallbackEpoch))._3 | |
351 | 392 | } | |
352 | 393 | } | |
353 | 394 | ||
354 | 395 | ||
355 | 396 | func supportingBalance (chainId) = { | |
356 | 397 | func addBalance (acc,generatorStr) = { | |
357 | - | let $ | |
358 | - | let totalBalance = $ | |
359 | - | let generators = $ | |
398 | + | let $t01200212038 = acc | |
399 | + | let totalBalance = $t01200212038._1 | |
400 | + | let generators = $t01200212038._2 | |
360 | 401 | let generator = addressFromStringValue(generatorStr) | |
361 | 402 | if (containsElement(generators, generator)) | |
362 | 403 | then acc | |
363 | 404 | else { | |
364 | 405 | let balance = generatingBalance(generator) | |
365 | 406 | $Tuple2((totalBalance + balance), (generators :+ generator)) | |
366 | 407 | } | |
367 | 408 | } | |
368 | 409 | ||
369 | 410 | let allGenerators = split_4C(getStringValue(supportersKey(chainId)), SEP) | |
370 | - | let $ | |
411 | + | let $t01236012425 = { | |
371 | 412 | let $l = allGenerators | |
372 | 413 | let $s = size($l) | |
373 | 414 | let $acc0 = $Tuple2(0, nil) | |
374 | 415 | func $f0_1 ($a,$i) = if (($i >= $s)) | |
375 | 416 | then $a | |
376 | 417 | else addBalance($a, $l[$i]) | |
377 | 418 | ||
378 | 419 | func $f0_2 ($a,$i) = if (($i >= $s)) | |
379 | 420 | then $a | |
380 | 421 | else throw("List size exceeds 100") | |
381 | 422 | ||
382 | 423 | $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) | |
383 | 424 | } | |
384 | - | let balance = $ | |
385 | - | let _g = $ | |
425 | + | let balance = $t01236012425._1 | |
426 | + | let _g = $t01236012425._2 | |
386 | 427 | balance | |
387 | 428 | } | |
388 | 429 | ||
389 | 430 | ||
390 | 431 | func isContractSetup () = isDefined(getInteger(minerRewardKey)) | |
391 | 432 | ||
392 | 433 | ||
393 | 434 | func ensureMiningEpoch (generator) = if ((toString(generator) != computedGenerator)) | |
394 | 435 | then throw(((((toBase58String(generator.bytes) + " is not allowed to mine in ") + toString(height)) + " epoch. Expected ") + computedGenerator)) | |
395 | 436 | else unit | |
396 | 437 | ||
397 | 438 | ||
398 | 439 | func isReferenceCorrect (reference,lastBlock) = if ((reference == lastBlock)) | |
399 | 440 | then unit | |
400 | 441 | else throw(((("Expected a reference to the chain last block: 0x" + lastBlock) + ". Got: 0x") + reference)) | |
401 | 442 | ||
402 | 443 | ||
403 | 444 | func chainIsInactive (chainId) = { | |
404 | 445 | let firstBlockId = getStringValue(chainFirstBlockIdKey(chainId)) | |
405 | 446 | let firstValidAltChainId = valueOrElse(getInteger(firstValidAltChainIdKey), 0) | |
406 | 447 | if ((firstValidAltChainId > chainId)) | |
407 | 448 | then true | |
408 | 449 | else (blockMeta(getStringValue(finalizedBlockKey))._1 > blockMeta(firstBlockId)._1) | |
409 | 450 | } | |
410 | 451 | ||
411 | 452 | ||
412 | 453 | func minerChainId (miner) = valueOrElse(getInteger(minerChainIdKey(miner)), getInteger(("chainIdOf" + toString(miner)))) | |
413 | 454 | ||
414 | 455 | ||
415 | 456 | func ensureExpectedOrInactiveChain (generator,expectedChainId,checkHeightBlock) = { | |
416 | 457 | let heightIsCorrect = match checkHeightBlock { | |
417 | 458 | case blockHash: String => | |
418 | 459 | let lastMinedBlockHeight = lastHeightBy(generator, mainChainId) | |
419 | 460 | ((blockMeta(blockHash)._1 + 1) > lastMinedBlockHeight) | |
420 | 461 | case _ => | |
421 | 462 | true | |
422 | 463 | } | |
423 | 464 | match minerChainId(generator) { | |
424 | 465 | case currentId: Int => | |
425 | 466 | if (if ((currentId == expectedChainId)) | |
426 | 467 | then true | |
427 | 468 | else if (chainIsInactive(currentId)) | |
428 | 469 | then heightIsCorrect | |
429 | 470 | else false) | |
430 | 471 | then unit | |
431 | 472 | else throw(("miner is mining other chain " + toString(currentId))) | |
432 | 473 | case _ => | |
433 | 474 | unit | |
434 | 475 | } | |
435 | 476 | } | |
436 | 477 | ||
437 | 478 | ||
438 | 479 | func ensureCorrectEpoch (epoch) = if ((epoch == height)) | |
439 | 480 | then unit | |
440 | 481 | else throw(((("Expected block from epoch " + toString(height)) + ". Got ") + toString(epoch))) | |
441 | 482 | ||
442 | 483 | ||
443 | 484 | func addSupporter (chainId,generator) = { | |
444 | 485 | let supportersStr = getStringValue(supportersKey(chainId)) | |
445 | 486 | let supporters = split_4C(supportersStr, SEP) | |
446 | 487 | if (containsElement(supporters, toString(generator))) | |
447 | 488 | then nil | |
448 | 489 | else [StringEntry(supportersKey(chainId), ((supportersStr + SEP) + toString(generator)))] | |
449 | 490 | } | |
450 | 491 | ||
451 | 492 | ||
452 | 493 | func setOrFail (flags,index) = if ((0 > index)) | |
453 | 494 | then throw(("Can't withdraw at negative index: " + toString(index))) | |
454 | 495 | else { | |
455 | 496 | let flagsSize = size(flags) | |
456 | 497 | if ((index >= flagsSize)) | |
457 | 498 | then { | |
458 | 499 | let addZeroes = (index - flagsSize) | |
459 | 500 | if ((addZeroes > size(zeroesStr))) | |
460 | 501 | then throw((("Can't add " + toString(addZeroes)) + " empty flags. Contact with developers")) | |
461 | 502 | else ((flags + take(zeroesStr, addZeroes)) + "1") | |
462 | 503 | } | |
463 | 504 | else { | |
464 | 505 | let tail = drop(flags, index) | |
465 | 506 | let atIndex = take(tail, 1) | |
466 | 507 | if ((atIndex == "0")) | |
467 | 508 | then ((take(flags, index) + "1") + drop(tail, 1)) | |
468 | 509 | else throw((("Transfer #" + toString(index)) + " has been already taken")) | |
469 | 510 | } | |
470 | 511 | } | |
471 | 512 | ||
472 | 513 | ||
473 | 514 | func validateBlockHash (hexStr) = { | |
474 | 515 | let decodedBytes = fromBase16String(hexStr) | |
475 | 516 | if ((size(decodedBytes) != 32)) | |
476 | 517 | then throw("invalid block id length") | |
477 | 518 | else hexStr | |
478 | 519 | } | |
479 | 520 | ||
480 | 521 | ||
481 | 522 | func getUpdateFinalizedBlockAction (caller,newBlockHashHex,prevEpoch) = { | |
482 | 523 | let curFinalizedBlockHeight = blockMeta(getStringValue(finalizedBlockKey))._1 | |
483 | 524 | let newFinalizedBlockHash = calculateFinalizedBlockHash(caller, prevEpoch, newBlockHashHex) | |
484 | 525 | if (if ((newFinalizedBlockHash == newBlockHashHex)) | |
485 | 526 | then true | |
486 | 527 | else (blockMeta(newFinalizedBlockHash)._1 > curFinalizedBlockHeight)) | |
487 | 528 | then [StringEntry(finalizedBlockKey, newFinalizedBlockHash)] | |
488 | 529 | else nil | |
489 | 530 | } | |
490 | 531 | ||
491 | 532 | ||
492 | 533 | @Callable(i) | |
493 | - | func appendBlock (blockHashHex,referenceHex) = { | |
494 | - | let | |
495 | - | if (( | |
534 | + | func appendBlock_v2 (blockHashHex,referenceHex,elToClTransfersRootHashHex) = { | |
535 | + | let checkClToEl = requireClToElTransfersDisabled() | |
536 | + | if ((checkClToEl == checkClToEl)) | |
496 | 537 | then if ((thisEpochMiner != i.originCaller)) | |
497 | 538 | then match thisEpochMiner { | |
498 | 539 | case epochMiner: Address => | |
499 | 540 | throw(("not allowed to forge blocks in this epoch, expected from " + toString(epochMiner))) | |
500 | 541 | case _ => | |
501 | 542 | throw("not allowed to forge blocks in this epoch, epoch miner is absent") | |
502 | 543 | } | |
503 | 544 | else { | |
504 | 545 | let chainId = valueOrElse(minerChainId(i.originCaller), mainChainId) | |
505 | - | let $ | |
506 | - | let chainHeight = $ | |
507 | - | let lastBlockId = $ | |
546 | + | let $t01658216633 = chainMeta(chainId) | |
547 | + | let chainHeight = $t01658216633._1 | |
548 | + | let lastBlockId = $t01658216633._2 | |
508 | 549 | let checkReference = isReferenceCorrect(referenceHex, lastBlockId) | |
509 | 550 | if ((checkReference == checkReference)) | |
510 | 551 | then { | |
511 | 552 | let newChainHeight = (chainHeight + 1) | |
512 | - | let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(lastBlockId)) + i.originCaller.bytes) | |
513 | 553 | let checkBlockHash = validateBlockHash(blockHashHex) | |
514 | 554 | if ((checkBlockHash == checkBlockHash)) | |
515 | - | then [ | |
555 | + | then [mkBlockMetaEntry(blockHashHex, newChainHeight, lastBlockId, i.originCaller, chainId, elToClTransfersRootHashHex, -1), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight), mkChainMetaEntry(chainId, newChainHeight, blockHashHex), StringEntry(epochMetaKey(height), ((((toString(value(thisEpochMiner)) + SEP) + toString(thisEpochRef)) + SEP) + blockHashHex))] | |
516 | 556 | else throw("Strict value is not equal to itself.") | |
517 | 557 | } | |
518 | 558 | else throw("Strict value is not equal to itself.") | |
519 | 559 | } | |
520 | 560 | else throw("Strict value is not equal to itself.") | |
521 | 561 | } | |
522 | 562 | ||
523 | 563 | ||
524 | 564 | ||
525 | 565 | @Callable(i) | |
526 | - | func appendBlock_v2 (blockHashHex,referenceHex,elToClTransfersRootHashHex) = { | |
527 | - | let checkVersion = requireElToClTransfersEnabled() | |
528 | - | if ((checkVersion == checkVersion)) | |
529 | - | then if ((thisEpochMiner != i.originCaller)) | |
530 | - | then match thisEpochMiner { | |
531 | - | case epochMiner: Address => | |
532 | - | throw(("not allowed to forge blocks in this epoch, expected from " + toString(epochMiner))) | |
533 | - | case _ => | |
534 | - | throw("not allowed to forge blocks in this epoch, epoch miner is absent") | |
566 | + | func appendBlock_v3 (blockHashHex,referenceHex,elToClTransfersRootHashHex,lastClToElTransferIndex) = { | |
567 | + | let checkElToCl = requireClToElTransfersEnabled() | |
568 | + | if ((checkElToCl == checkElToCl)) | |
569 | + | then { | |
570 | + | let checkCaller = if ((thisEpochMiner == i.originCaller)) | |
571 | + | then true | |
572 | + | else match thisEpochMiner { | |
573 | + | case epochMiner: Address => | |
574 | + | throw(("not allowed to forge blocks in this epoch, expected from " + toString(epochMiner))) | |
575 | + | case _ => | |
576 | + | throw("not allowed to forge blocks in this epoch, epoch miner is absent") | |
577 | + | } | |
578 | + | if ((checkCaller == checkCaller)) | |
579 | + | then { | |
580 | + | let chainId = valueOrElse(minerChainId(i.originCaller), mainChainId) | |
581 | + | let $t01786017911 = chainMeta(chainId) | |
582 | + | let chainHeight = $t01786017911._1 | |
583 | + | let lastBlockId = $t01786017911._2 | |
584 | + | let checkReference = isReferenceCorrect(referenceHex, lastBlockId) | |
585 | + | if ((checkReference == checkReference)) | |
586 | + | then { | |
587 | + | let checkTransfers = ensureCorrectTransfers(blockMeta(referenceHex)._7, lastClToElTransferIndex, false) | |
588 | + | if ((checkTransfers == checkTransfers)) | |
589 | + | then { | |
590 | + | let newChainHeight = (chainHeight + 1) | |
591 | + | let checkBlockHash = validateBlockHash(blockHashHex) | |
592 | + | if ((checkBlockHash == checkBlockHash)) | |
593 | + | then [mkBlockMetaEntry(blockHashHex, newChainHeight, lastBlockId, i.originCaller, chainId, elToClTransfersRootHashHex, lastClToElTransferIndex), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight), mkChainMetaEntry(chainId, newChainHeight, blockHashHex), StringEntry(epochMetaKey(height), ((((toString(value(thisEpochMiner)) + SEP) + toString(thisEpochRef)) + SEP) + blockHashHex))] | |
594 | + | else throw("Strict value is not equal to itself.") | |
595 | + | } | |
596 | + | else throw("Strict value is not equal to itself.") | |
597 | + | } | |
598 | + | else throw("Strict value is not equal to itself.") | |
599 | + | } | |
600 | + | else throw("Strict value is not equal to itself.") | |
535 | 601 | } | |
536 | - | else { | |
537 | - | let chainId = valueOrElse(minerChainId(i.originCaller), mainChainId) | |
538 | - | let $t01596916020 = chainMeta(chainId) | |
539 | - | let chainHeight = $t01596916020._1 | |
540 | - | let lastBlockId = $t01596916020._2 | |
541 | - | let checkReference = isReferenceCorrect(referenceHex, lastBlockId) | |
542 | - | if ((checkReference == checkReference)) | |
543 | - | then { | |
544 | - | let newChainHeight = (chainHeight + 1) | |
545 | - | let checkBlockHash = validateBlockHash(blockHashHex) | |
546 | - | if ((checkBlockHash == checkBlockHash)) | |
547 | - | then [mkBlockMetaEntry(blockHashHex, newChainHeight, lastBlockId, i.originCaller, chainId, elToClTransfersRootHashHex), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight), StringEntry(chainMetaKey(chainId), ((toString(newChainHeight) + SEP) + blockHashHex)), StringEntry(epochMetaKey(height), ((((toString(value(thisEpochMiner)) + SEP) + toString(thisEpochRef)) + SEP) + blockHashHex))] | |
548 | - | else throw("Strict value is not equal to itself.") | |
549 | - | } | |
550 | - | else throw("Strict value is not equal to itself.") | |
551 | - | } | |
552 | 602 | else throw("Strict value is not equal to itself.") | |
553 | 603 | } | |
554 | 604 | ||
555 | 605 | ||
556 | 606 | ||
557 | 607 | @Callable(i) | |
558 | - | func extendMainChain (blockHashHex,referenceHex,epoch) = { | |
559 | - | let | |
560 | - | if (( | |
608 | + | func extendMainChain_v2 (blockHashHex,referenceHex,epoch,elToClTransfersRootHashHex) = { | |
609 | + | let checkClToEl = requireClToElTransfersDisabled() | |
610 | + | if ((checkClToEl == checkClToEl)) | |
561 | 611 | then { | |
562 | 612 | let checkBlockHash = validateBlockHash(blockHashHex) | |
563 | 613 | if ((checkBlockHash == checkBlockHash)) | |
564 | 614 | then { | |
565 | 615 | let checkEpoch = ensureCorrectEpoch(epoch) | |
566 | 616 | if ((checkEpoch == checkEpoch)) | |
567 | 617 | then { | |
568 | 618 | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
569 | 619 | if ((checkGenerator == checkGenerator)) | |
570 | 620 | then { | |
571 | 621 | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, mainChainId, unit) | |
572 | 622 | if ((checkChain == checkChain)) | |
573 | 623 | then { | |
574 | 624 | let checkReference = isReferenceCorrect(referenceHex, mainChainLastBlock) | |
575 | 625 | if ((checkReference == checkReference)) | |
576 | 626 | then { | |
577 | 627 | let thisEpochMeta = match epochMeta(height) { | |
578 | 628 | case _: Unit => | |
579 | 629 | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(mainChainEpoch)) + SEP) + blockHashHex)) | |
580 | 630 | case other => | |
581 | 631 | throw("Epoch already started") | |
582 | 632 | } | |
583 | 633 | let updateFinalizedBlock = getUpdateFinalizedBlockAction(i.originCaller, blockHashHex, mainChainEpoch) | |
584 | 634 | let newChainHeight = (mainChainHeight + 1) | |
585 | - | let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(mainChainLastBlock)) + i.originCaller.bytes) | |
586 | - | ([BinaryEntry((blockMetaK + blockHashHex), newBlockMeta), StringEntry(chainMetaKey(mainChainId), ((toString(newChainHeight) + SEP) + blockHashHex)), IntegerEntry(minerChainIdKey(i.originCaller), mainChainId), IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), newChainHeight), thisEpochMeta] ++ updateFinalizedBlock) | |
635 | + | ([mkBlockMetaEntry(blockHashHex, newChainHeight, mainChainLastBlock, i.originCaller, mainChainId, elToClTransfersRootHashHex, -1), mkChainMetaEntry(mainChainId, newChainHeight, blockHashHex), IntegerEntry(minerChainIdKey(i.originCaller), mainChainId), IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), newChainHeight), thisEpochMeta] ++ updateFinalizedBlock) | |
587 | 636 | } | |
588 | 637 | else throw("Strict value is not equal to itself.") | |
589 | 638 | } | |
590 | 639 | else throw("Strict value is not equal to itself.") | |
591 | 640 | } | |
592 | 641 | else throw("Strict value is not equal to itself.") | |
593 | 642 | } | |
594 | 643 | else throw("Strict value is not equal to itself.") | |
595 | 644 | } | |
596 | 645 | else throw("Strict value is not equal to itself.") | |
597 | 646 | } | |
598 | 647 | else throw("Strict value is not equal to itself.") | |
599 | 648 | } | |
600 | 649 | ||
601 | 650 | ||
602 | 651 | ||
603 | 652 | @Callable(i) | |
604 | - | func extendMainChain_ | |
605 | - | let | |
606 | - | if (( | |
653 | + | func extendMainChain_v3 (blockHashHex,referenceHex,epoch,elToClTransfersRootHashHex,lastClToElTransferIndex) = { | |
654 | + | let checkElToCl = requireClToElTransfersEnabled() | |
655 | + | if ((checkElToCl == checkElToCl)) | |
607 | 656 | then { | |
608 | 657 | let checkBlockHash = validateBlockHash(blockHashHex) | |
609 | 658 | if ((checkBlockHash == checkBlockHash)) | |
610 | 659 | then { | |
611 | 660 | let checkEpoch = ensureCorrectEpoch(epoch) | |
612 | 661 | if ((checkEpoch == checkEpoch)) | |
613 | 662 | then { | |
614 | 663 | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
615 | 664 | if ((checkGenerator == checkGenerator)) | |
616 | 665 | then { | |
617 | 666 | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, mainChainId, unit) | |
618 | 667 | if ((checkChain == checkChain)) | |
619 | 668 | then { | |
620 | 669 | let checkReference = isReferenceCorrect(referenceHex, mainChainLastBlock) | |
621 | 670 | if ((checkReference == checkReference)) | |
622 | 671 | then { | |
623 | - | let thisEpochMeta = match epochMeta(height) { | |
624 | - | case _: Unit => | |
625 | - | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(mainChainEpoch)) + SEP) + blockHashHex)) | |
626 | - | case other => | |
627 | - | throw("Epoch already started") | |
628 | - | } | |
629 | - | let updateFinalizedBlock = getUpdateFinalizedBlockAction(i.originCaller, blockHashHex, mainChainEpoch) | |
630 | - | let newChainHeight = (mainChainHeight + 1) | |
631 | - | ([mkBlockMetaEntry(blockHashHex, newChainHeight, mainChainLastBlock, i.originCaller, mainChainId, elToClTransfersRootHashHex), StringEntry(chainMetaKey(mainChainId), ((toString(newChainHeight) + SEP) + blockHashHex)), IntegerEntry(minerChainIdKey(i.originCaller), mainChainId), IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), newChainHeight), thisEpochMeta] ++ updateFinalizedBlock) | |
672 | + | let checkTransfers = ensureCorrectTransfers(blockMeta(referenceHex)._7, lastClToElTransferIndex, true) | |
673 | + | if ((checkTransfers == checkTransfers)) | |
674 | + | then { | |
675 | + | let thisEpochMeta = match epochMeta(height) { | |
676 | + | case _: Unit => | |
677 | + | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(mainChainEpoch)) + SEP) + blockHashHex)) | |
678 | + | case other => | |
679 | + | throw("Epoch already started") | |
680 | + | } | |
681 | + | if ((thisEpochMeta == thisEpochMeta)) | |
682 | + | then { | |
683 | + | let updateFinalizedBlock = getUpdateFinalizedBlockAction(i.originCaller, blockHashHex, mainChainEpoch) | |
684 | + | let newChainHeight = (mainChainHeight + 1) | |
685 | + | ([mkBlockMetaEntry(blockHashHex, newChainHeight, mainChainLastBlock, i.originCaller, mainChainId, elToClTransfersRootHashHex, lastClToElTransferIndex), mkChainMetaEntry(mainChainId, newChainHeight, blockHashHex), IntegerEntry(minerChainIdKey(i.originCaller), mainChainId), IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), newChainHeight), thisEpochMeta] ++ updateFinalizedBlock) | |
686 | + | } | |
687 | + | else throw("Strict value is not equal to itself.") | |
688 | + | } | |
689 | + | else throw("Strict value is not equal to itself.") | |
632 | 690 | } | |
633 | 691 | else throw("Strict value is not equal to itself.") | |
634 | 692 | } | |
635 | 693 | else throw("Strict value is not equal to itself.") | |
636 | 694 | } | |
637 | 695 | else throw("Strict value is not equal to itself.") | |
638 | 696 | } | |
639 | 697 | else throw("Strict value is not equal to itself.") | |
640 | 698 | } | |
641 | 699 | else throw("Strict value is not equal to itself.") | |
642 | 700 | } | |
643 | 701 | else throw("Strict value is not equal to itself.") | |
644 | 702 | } | |
645 | 703 | ||
646 | 704 | ||
647 | 705 | ||
648 | 706 | @Callable(i) | |
649 | - | func startAltChain (blockHashHex,referenceHex,epoch) = { | |
650 | - | let | |
651 | - | if (( | |
707 | + | func startAltChain_v2 (blockHashHex,referenceHex,epoch,elToClTransfersRootHashHex) = { | |
708 | + | let checkClToEl = requireClToElTransfersDisabled() | |
709 | + | if ((checkClToEl == checkClToEl)) | |
652 | 710 | then { | |
653 | 711 | let checkBlockHash = validateBlockHash(blockHashHex) | |
654 | 712 | if ((checkBlockHash == checkBlockHash)) | |
655 | 713 | then { | |
656 | 714 | let checkEpoch = ensureCorrectEpoch(epoch) | |
657 | 715 | if ((checkEpoch == checkEpoch)) | |
658 | 716 | then { | |
659 | 717 | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
660 | 718 | if ((checkGenerator == checkGenerator)) | |
661 | 719 | then { | |
662 | - | let $t01969819802 = blockMeta(referenceHex) | |
663 | - | let refChainHeight = $t01969819802._1 | |
664 | - | let refEpoch = $t01969819802._2 | |
665 | - | let refRef = $t01969819802._3 | |
666 | - | let refGenerator = $t01969819802._4 | |
667 | - | let refIgnored1 = $t01969819802._5 | |
668 | - | let refIgnored2 = $t01969819802._6 | |
720 | + | let $t02180521883 = blockMeta(referenceHex) | |
721 | + | let refChainHeight = $t02180521883._1 | |
722 | + | let refEpoch = $t02180521883._2 | |
723 | + | let refRef = $t02180521883._3 | |
724 | + | let refGenerator = $t02180521883._4 | |
669 | 725 | let finalizedEpoch = blockMeta(getStringValue(finalizedBlockKey))._2 | |
670 | 726 | let epochRef = if ((refEpoch >= finalizedEpoch)) | |
671 | 727 | then refEpoch | |
672 | 728 | else throw((((("can not start alt chain from epoch " + toString(refEpoch)) + ", epoch ") + toString(finalizedEpoch)) + " is finalized")) | |
673 | 729 | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, mainChainId, referenceHex) | |
674 | 730 | if ((checkChain == checkChain)) | |
675 | 731 | then { | |
676 | 732 | let newChainId = (valueOrElse(getInteger(lastChainIdKey), 0) + 1) | |
677 | 733 | let newChainHeight = (blockMeta(referenceHex)._1 + 1) | |
678 | - | let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(referenceHex)) + i.originCaller.bytes) | |
679 | 734 | let thisEpochMeta = match epochMeta(height) { | |
680 | 735 | case _: Unit => | |
681 | 736 | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(epochRef)) + SEP) + blockHashHex)) | |
682 | 737 | case other => | |
683 | 738 | throw("Epoch already started") | |
684 | 739 | } | |
685 | - | [thisEpochMeta, | |
740 | + | [thisEpochMeta, mkBlockMetaEntry(blockHashHex, newChainHeight, referenceHex, i.originCaller, newChainId, elToClTransfersRootHashHex, -1), 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)] | |
686 | 741 | } | |
687 | 742 | else throw("Strict value is not equal to itself.") | |
688 | 743 | } | |
689 | 744 | else throw("Strict value is not equal to itself.") | |
690 | 745 | } | |
691 | 746 | else throw("Strict value is not equal to itself.") | |
692 | 747 | } | |
693 | 748 | else throw("Strict value is not equal to itself.") | |
694 | 749 | } | |
695 | 750 | else throw("Strict value is not equal to itself.") | |
696 | 751 | } | |
697 | 752 | ||
698 | 753 | ||
699 | 754 | ||
700 | 755 | @Callable(i) | |
701 | - | func startAltChain_ | |
702 | - | let | |
703 | - | if (( | |
756 | + | func startAltChain_v3 (blockHashHex,referenceHex,epoch,elToClTransfersRootHashHex,lastClToElTransferIndex) = { | |
757 | + | let checkElToCl = requireClToElTransfersEnabled() | |
758 | + | if ((checkElToCl == checkElToCl)) | |
704 | 759 | then { | |
705 | 760 | let checkBlockHash = validateBlockHash(blockHashHex) | |
706 | 761 | if ((checkBlockHash == checkBlockHash)) | |
707 | 762 | then { | |
708 | 763 | let checkEpoch = ensureCorrectEpoch(epoch) | |
709 | 764 | if ((checkEpoch == checkEpoch)) | |
710 | 765 | then { | |
711 | 766 | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
712 | 767 | if ((checkGenerator == checkGenerator)) | |
713 | 768 | then { | |
714 | - | let $t02161221716 = blockMeta(referenceHex) | |
715 | - | let refChainHeight = $t02161221716._1 | |
716 | - | let refEpoch = $t02161221716._2 | |
717 | - | let refRef = $t02161221716._3 | |
718 | - | let refGenerator = $t02161221716._4 | |
719 | - | let refIgnored1 = $t02161221716._5 | |
720 | - | let refIgnored2 = $t02161221716._6 | |
769 | + | let $t02364123763 = blockMeta(referenceHex) | |
770 | + | let refChainHeight = $t02364123763._1 | |
771 | + | let refEpoch = $t02364123763._2 | |
772 | + | let refRef = $t02364123763._3 | |
773 | + | let refGenerator = $t02364123763._4 | |
774 | + | let refIgnored5 = $t02364123763._5 | |
775 | + | let refIgnored6 = $t02364123763._6 | |
776 | + | let refTransferIndex = $t02364123763._7 | |
721 | 777 | let finalizedEpoch = blockMeta(getStringValue(finalizedBlockKey))._2 | |
722 | 778 | let epochRef = if ((refEpoch >= finalizedEpoch)) | |
723 | 779 | then refEpoch | |
724 | - | else throw(((((" | |
780 | + | else throw((((("Can not start alt chain from epoch " + toString(refEpoch)) + ", epoch ") + toString(finalizedEpoch)) + " is finalized")) | |
725 | 781 | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, mainChainId, referenceHex) | |
726 | 782 | if ((checkChain == checkChain)) | |
727 | 783 | then { | |
728 | - | let newChainId = (valueOrElse(getInteger(lastChainIdKey), 0) + 1) | |
729 | - | let newChainHeight = (blockMeta(referenceHex)._1 + 1) | |
730 | - | let thisEpochMeta = match epochMeta(height) { | |
731 | - | case _: Unit => | |
732 | - | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(epochRef)) + SEP) + blockHashHex)) | |
733 | - | case other => | |
734 | - | throw("Epoch already started") | |
735 | - | } | |
736 | - | [thisEpochMeta, mkBlockMetaEntry(blockHashHex, newChainHeight, referenceHex, i.originCaller, newChainId, elToClTransfersRootHashHex), StringEntry(chainFirstBlockIdKey(newChainId), blockHashHex), StringEntry(chainMetaKey(newChainId), ((toString(newChainHeight) + SEP) + 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)] | |
784 | + | let checkTransfers = ensureCorrectTransfers(refTransferIndex, lastClToElTransferIndex, true) | |
785 | + | if ((checkTransfers == checkTransfers)) | |
786 | + | then { | |
787 | + | let newChainId = (valueOrElse(getInteger(lastChainIdKey), 0) + 1) | |
788 | + | let newChainHeight = (refChainHeight + 1) | |
789 | + | let thisEpochMeta = match epochMeta(height) { | |
790 | + | case _: Unit => | |
791 | + | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(epochRef)) + SEP) + blockHashHex)) | |
792 | + | case other => | |
793 | + | throw("Epoch already started") | |
794 | + | } | |
795 | + | [thisEpochMeta, mkBlockMetaEntry(blockHashHex, newChainHeight, referenceHex, i.originCaller, newChainId, elToClTransfersRootHashHex, lastClToElTransferIndex), 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)] | |
796 | + | } | |
797 | + | else throw("Strict value is not equal to itself.") | |
737 | 798 | } | |
738 | 799 | else throw("Strict value is not equal to itself.") | |
739 | 800 | } | |
740 | 801 | else throw("Strict value is not equal to itself.") | |
741 | 802 | } | |
742 | 803 | else throw("Strict value is not equal to itself.") | |
743 | 804 | } | |
744 | 805 | else throw("Strict value is not equal to itself.") | |
745 | 806 | } | |
746 | 807 | else throw("Strict value is not equal to itself.") | |
747 | 808 | } | |
748 | 809 | ||
749 | 810 | ||
750 | 811 | ||
751 | 812 | @Callable(i) | |
752 | - | func extendAltChain (chainId,blockHashHex,referenceHex,epoch) = { | |
753 | - | let | |
754 | - | if (( | |
813 | + | func extendAltChain_v2 (chainId,blockHashHex,referenceHex,epoch,elToClTransfersRootHashHex) = { | |
814 | + | let checkClToEl = requireClToElTransfersDisabled() | |
815 | + | if ((checkClToEl == checkClToEl)) | |
755 | 816 | then { | |
756 | 817 | let checkBlockHash = validateBlockHash(blockHashHex) | |
757 | 818 | if ((checkBlockHash == checkBlockHash)) | |
758 | 819 | then { | |
759 | 820 | let checkEpoch = ensureCorrectEpoch(epoch) | |
760 | 821 | if ((checkEpoch == checkEpoch)) | |
761 | 822 | then { | |
762 | 823 | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
763 | 824 | if ((checkGenerator == checkGenerator)) | |
764 | 825 | then { | |
765 | 826 | let chainFirstBlockMeta = blockMeta(getStringValue(chainFirstBlockIdKey(chainId))) | |
766 | 827 | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, chainId, toBase16String(chainFirstBlockMeta._3)) | |
767 | 828 | if ((checkChain == checkChain)) | |
768 | 829 | then { | |
769 | - | let $ | |
770 | - | let chainHeight = $ | |
771 | - | let chainLastBlock = $ | |
830 | + | let $t02613726191 = chainMeta(chainId) | |
831 | + | let chainHeight = $t02613726191._1 | |
832 | + | let chainLastBlock = $t02613726191._2 | |
772 | 833 | let checkReference = isReferenceCorrect(referenceHex, chainLastBlock) | |
773 | 834 | if ((checkReference == checkReference)) | |
774 | 835 | then { | |
775 | 836 | let newChainHeight = (chainHeight + 1) | |
776 | 837 | let prevEpoch = blockMeta(referenceHex)._2 | |
777 | 838 | let updateMainChainData = if ((supportingBalance(chainId) > (computedTotalBalance / 2))) | |
778 | 839 | then { | |
779 | 840 | let lastChainId = valueOrElse(getInteger(lastChainIdKey), 0) | |
780 | 841 | let updateFinalizedBlock = getUpdateFinalizedBlockAction(i.originCaller, blockHashHex, prevEpoch) | |
781 | 842 | ([IntegerEntry(chainForkedHeightKey(mainChainId), chainFirstBlockMeta._1), IntegerEntry(mainChainIdKey, chainId), IntegerEntry(firstValidAltChainIdKey, (lastChainId + 1))] ++ updateFinalizedBlock) | |
782 | 843 | } | |
783 | 844 | else nil | |
784 | 845 | let thisEpochMeta = match epochMeta(height) { | |
785 | 846 | case _: Unit => | |
786 | 847 | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(prevEpoch)) + SEP) + blockHashHex)) | |
787 | 848 | case other => | |
788 | 849 | throw("Epoch already started") | |
789 | 850 | } | |
790 | 851 | let updateMainChainLastMinedBlock = if (if ((updateMainChainData == nil)) | |
791 | 852 | then (valueOrElse(minerChainId(i.originCaller), 0) != chainId) | |
792 | 853 | else false) | |
793 | 854 | then [IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), chainFirstBlockMeta._1)] | |
794 | 855 | else nil | |
795 | - | let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(referenceHex)) + i.originCaller.bytes) | |
796 | - | ((([BinaryEntry((blockMetaK + blockHashHex), newBlockMeta), StringEntry(chainMetaKey(chainId), ((toString(newChainHeight) + SEP) + blockHashHex)), thisEpochMeta, IntegerEntry(minerChainIdKey(i.originCaller), chainId), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight)] ++ updateMainChainData) ++ addSupporter(chainId, i.originCaller)) ++ updateMainChainLastMinedBlock) | |
856 | + | ((([mkBlockMetaEntry(blockHashHex, newChainHeight, referenceHex, i.originCaller, chainId, elToClTransfersRootHashHex, -1), mkChainMetaEntry(chainId, newChainHeight, blockHashHex), thisEpochMeta, IntegerEntry(minerChainIdKey(i.originCaller), chainId), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight)] ++ updateMainChainData) ++ addSupporter(chainId, i.originCaller)) ++ updateMainChainLastMinedBlock) | |
797 | 857 | } | |
798 | 858 | else throw("Strict value is not equal to itself.") | |
799 | 859 | } | |
800 | 860 | else throw("Strict value is not equal to itself.") | |
801 | 861 | } | |
802 | 862 | else throw("Strict value is not equal to itself.") | |
803 | 863 | } | |
804 | 864 | else throw("Strict value is not equal to itself.") | |
805 | 865 | } | |
806 | 866 | else throw("Strict value is not equal to itself.") | |
807 | 867 | } | |
808 | 868 | else throw("Strict value is not equal to itself.") | |
809 | 869 | } | |
810 | 870 | ||
811 | 871 | ||
812 | 872 | ||
813 | 873 | @Callable(i) | |
814 | - | func extendAltChain_ | |
815 | - | let | |
816 | - | if (( | |
874 | + | func extendAltChain_v3 (chainId,blockHashHex,referenceHex,epoch,elToClTransfersRootHashHex,lastClToElTransferIndex) = { | |
875 | + | let checkElToCl = requireClToElTransfersEnabled() | |
876 | + | if ((checkElToCl == checkElToCl)) | |
817 | 877 | then { | |
818 | 878 | let checkBlockHash = validateBlockHash(blockHashHex) | |
819 | 879 | if ((checkBlockHash == checkBlockHash)) | |
820 | 880 | then { | |
821 | 881 | let checkEpoch = ensureCorrectEpoch(epoch) | |
822 | 882 | if ((checkEpoch == checkEpoch)) | |
823 | 883 | then { | |
824 | 884 | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
825 | 885 | if ((checkGenerator == checkGenerator)) | |
826 | 886 | then { | |
827 | 887 | let chainFirstBlockMeta = blockMeta(getStringValue(chainFirstBlockIdKey(chainId))) | |
828 | 888 | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, chainId, toBase16String(chainFirstBlockMeta._3)) | |
829 | 889 | if ((checkChain == checkChain)) | |
830 | 890 | then { | |
831 | - | let $ | |
832 | - | let chainHeight = $ | |
833 | - | let chainLastBlock = $ | |
891 | + | let $t02873528789 = chainMeta(chainId) | |
892 | + | let chainHeight = $t02873528789._1 | |
893 | + | let chainLastBlock = $t02873528789._2 | |
834 | 894 | let checkReference = isReferenceCorrect(referenceHex, chainLastBlock) | |
835 | 895 | if ((checkReference == checkReference)) | |
836 | 896 | then { | |
837 | - | let newChainHeight = (chainHeight + 1) | |
838 | - | let prevEpoch = blockMeta(referenceHex)._2 | |
839 | - | let updateMainChainData = if ((supportingBalance(chainId) > (computedTotalBalance / 2))) | |
897 | + | let checkTransfers = ensureCorrectTransfers(blockMeta(referenceHex)._7, lastClToElTransferIndex, true) | |
898 | + | if ((checkTransfers == checkTransfers)) | |
840 | 899 | then { | |
841 | - | let lastChainId = valueOrElse(getInteger(lastChainIdKey), 0) | |
842 | - | let updateFinalizedBlock = getUpdateFinalizedBlockAction(i.originCaller, blockHashHex, prevEpoch) | |
843 | - | ([IntegerEntry(chainForkedHeightKey(mainChainId), chainFirstBlockMeta._1), IntegerEntry(mainChainIdKey, chainId), IntegerEntry(firstValidAltChainIdKey, (lastChainId + 1))] ++ updateFinalizedBlock) | |
900 | + | let newChainHeight = (chainHeight + 1) | |
901 | + | let prevEpoch = blockMeta(referenceHex)._2 | |
902 | + | let updateMainChainData = if ((supportingBalance(chainId) > (computedTotalBalance / 2))) | |
903 | + | then { | |
904 | + | let lastChainId = valueOrElse(getInteger(lastChainIdKey), 0) | |
905 | + | let updateFinalizedBlock = getUpdateFinalizedBlockAction(i.originCaller, blockHashHex, prevEpoch) | |
906 | + | ([IntegerEntry(chainForkedHeightKey(mainChainId), chainFirstBlockMeta._1), IntegerEntry(mainChainIdKey, chainId), IntegerEntry(firstValidAltChainIdKey, (lastChainId + 1))] ++ updateFinalizedBlock) | |
907 | + | } | |
908 | + | else nil | |
909 | + | let thisEpochMeta = match epochMeta(height) { | |
910 | + | case _: Unit => | |
911 | + | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(prevEpoch)) + SEP) + blockHashHex)) | |
912 | + | case other => | |
913 | + | throw("Epoch already started") | |
844 | 914 | } | |
845 | - | else nil | |
846 | - | let thisEpochMeta = match epochMeta(height) { | |
847 | - | case _: Unit => | |
848 | - | StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(prevEpoch)) + SEP) + blockHashHex)) | |
849 | - | case other => | |
850 | - | throw("Epoch already started") | |
851 | - | } | |
852 | - | let updateMainChainLastMinedBlock = if (if ((updateMainChainData == nil)) | |
853 | - | then (valueOrElse(minerChainId(i.originCaller), 0) != chainId) | |
854 | - | else false) | |
855 | - | then [IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), chainFirstBlockMeta._1)] | |
856 | - | else nil | |
857 | - | ((([mkBlockMetaEntry(blockHashHex, newChainHeight, referenceHex, i.originCaller, chainId, elToClTransfersRootHashHex), StringEntry(chainMetaKey(chainId), ((toString(newChainHeight) + SEP) + blockHashHex)), thisEpochMeta, IntegerEntry(minerChainIdKey(i.originCaller), chainId), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight)] ++ updateMainChainData) ++ addSupporter(chainId, i.originCaller)) ++ updateMainChainLastMinedBlock) | |
915 | + | if ((thisEpochMeta == thisEpochMeta)) | |
916 | + | then { | |
917 | + | let updateMainChainLastMinedBlock = if (if ((updateMainChainData == nil)) | |
918 | + | then (valueOrElse(minerChainId(i.originCaller), 0) != chainId) | |
919 | + | else false) | |
920 | + | then [IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), chainFirstBlockMeta._1)] | |
921 | + | else nil | |
922 | + | ((([mkBlockMetaEntry(blockHashHex, newChainHeight, referenceHex, i.originCaller, chainId, elToClTransfersRootHashHex, lastClToElTransferIndex), mkChainMetaEntry(chainId, newChainHeight, blockHashHex), thisEpochMeta, IntegerEntry(minerChainIdKey(i.originCaller), chainId), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight)] ++ updateMainChainData) ++ addSupporter(chainId, i.originCaller)) ++ updateMainChainLastMinedBlock) | |
923 | + | } | |
924 | + | else throw("Strict value is not equal to itself.") | |
925 | + | } | |
926 | + | else throw("Strict value is not equal to itself.") | |
858 | 927 | } | |
859 | 928 | else throw("Strict value is not equal to itself.") | |
860 | 929 | } | |
861 | 930 | else throw("Strict value is not equal to itself.") | |
862 | 931 | } | |
863 | 932 | else throw("Strict value is not equal to itself.") | |
864 | 933 | } | |
865 | 934 | else throw("Strict value is not equal to itself.") | |
866 | 935 | } | |
867 | 936 | else throw("Strict value is not equal to itself.") | |
868 | 937 | } | |
869 | 938 | else throw("Strict value is not equal to itself.") | |
870 | 939 | } | |
871 | 940 | ||
872 | 941 | ||
873 | 942 | ||
874 | 943 | @Callable(i) | |
875 | 944 | func join (rewardAddress) = { | |
876 | - | func | |
945 | + | func ensureNotOverrideOtherMinerPk (elAddressHex) = match getBinary(minerPkKey(elAddressHex)) { | |
877 | 946 | case pk: ByteVector => | |
878 | 947 | if ((pk == i.originCallerPublicKey)) | |
879 | 948 | then unit | |
880 | - | else throw((((" | |
949 | + | else throw(((("EL miner address " + elAddressHex) + " is already linked with ") + toBase58String(pk))) | |
881 | 950 | case _ => | |
882 | 951 | unit | |
883 | 952 | } | |
884 | 953 | ||
885 | 954 | if (!(isContractSetup())) | |
886 | 955 | then throw("The contract has not yet set up") | |
887 | 956 | else if ((MIN_BALANCE > wavesBalance(i.originCaller).generating)) | |
888 | 957 | then throw(((("Insufficient generating balance: " + toString(wavesBalance(i.originCaller).generating)) + ". Required: ") + toString(MIN_BALANCE))) | |
889 | 958 | else if ((size(rewardAddress) != 20)) | |
890 | 959 | then throw("rewardAddress should be an L2 address") | |
891 | 960 | else if ((size(allMiners) >= 50)) | |
892 | 961 | then throw("too many miners") | |
893 | 962 | else { | |
894 | 963 | func checkExistence (exists,miner) = if (exists) | |
895 | 964 | then true | |
896 | 965 | else (miner == toString(i.originCaller)) | |
897 | 966 | ||
898 | 967 | let alreadyExists = { | |
899 | 968 | let $l = allMiners | |
900 | 969 | let $s = size($l) | |
901 | 970 | let $acc0 = false | |
902 | 971 | func $f0_1 ($a,$i) = if (($i >= $s)) | |
903 | 972 | then $a | |
904 | 973 | else checkExistence($a, $l[$i]) | |
905 | 974 | ||
906 | 975 | func $f0_2 ($a,$i) = if (($i >= $s)) | |
907 | 976 | then $a | |
908 | 977 | else throw("List size exceeds 50") | |
909 | 978 | ||
910 | 979 | $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) | |
911 | 980 | } | |
912 | 981 | if (alreadyExists) | |
913 | 982 | then nil | |
914 | 983 | else { | |
915 | 984 | let newMiner = toString(i.originCaller) | |
916 | 985 | let rewardAddressHex = toBase16String(rewardAddress) | |
917 | - | let check = | |
986 | + | let check = ensureNotOverrideOtherMinerPk(rewardAddressHex) | |
918 | 987 | if ((check == check)) | |
919 | 988 | then { | |
920 | 989 | let newMiners = if ((size(allMiners) == 0)) | |
921 | 990 | then newMiner | |
922 | 991 | else ((allMinersStr + SEP) + newMiner) | |
923 | 992 | let deleteOldRewardAddressPk = match getString(minerRewardAddressKey(newMiner)) { | |
924 | 993 | case oldAddress: String => | |
925 | 994 | if ((oldAddress == toBase16String(rewardAddress))) | |
926 | 995 | then nil | |
927 | 996 | else [DeleteEntry(minerPkKey(oldAddress))] | |
928 | 997 | case _ => | |
929 | 998 | nil | |
930 | 999 | } | |
931 | 1000 | ([StringEntry(allMinersKey, newMiners), StringEntry(minerRewardAddressKey(newMiner), ("0x" + rewardAddressHex)), BinaryEntry(minerPkKey(rewardAddressHex), i.originCallerPublicKey)] ++ deleteOldRewardAddressPk) | |
932 | 1001 | } | |
933 | 1002 | else throw("Strict value is not equal to itself.") | |
934 | 1003 | } | |
935 | 1004 | } | |
936 | 1005 | } | |
937 | 1006 | ||
938 | 1007 | ||
939 | 1008 | ||
940 | 1009 | @Callable(i) | |
941 | 1010 | func leave () = { | |
942 | 1011 | let leavingMiner = toString(i.originCaller) | |
943 | 1012 | func skipLeavingMiner (acc,miner) = if ((miner == leavingMiner)) | |
944 | 1013 | then acc | |
945 | 1014 | else (acc :+ miner) | |
946 | 1015 | ||
947 | 1016 | let remainingMiners = { | |
948 | 1017 | let $l = allMiners | |
949 | 1018 | let $s = size($l) | |
950 | 1019 | let $acc0 = nil | |
951 | 1020 | func $f0_1 ($a,$i) = if (($i >= $s)) | |
952 | 1021 | then $a | |
953 | 1022 | else skipLeavingMiner($a, $l[$i]) | |
954 | 1023 | ||
955 | 1024 | func $f0_2 ($a,$i) = if (($i >= $s)) | |
956 | 1025 | then $a | |
957 | 1026 | else throw("List size exceeds 50") | |
958 | 1027 | ||
959 | 1028 | $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) | |
960 | 1029 | } | |
961 | 1030 | let rewardAddrKey = minerRewardAddressKey(leavingMiner) | |
962 | 1031 | let prevRewardAddress = valueOrErrorMessage(getString(this, rewardAddrKey), "miner has never joined") | |
963 | 1032 | if ((thisEpochMiner == i.originCaller)) | |
964 | 1033 | then throw("designated miner can't leave") | |
965 | - | else [StringEntry(allMinersKey, makeString_2C(remainingMiners, SEP)), DeleteEntry(rewardAddrKey), DeleteEntry(minerPkKey(prevRewardAddress))] | |
1034 | + | else [StringEntry(allMinersKey, makeString_2C(remainingMiners, SEP))] | |
1035 | + | } | |
1036 | + | ||
1037 | + | ||
1038 | + | ||
1039 | + | @Callable(i) | |
1040 | + | func transfer (destElAddressHex) = { | |
1041 | + | let checkClToEl = requireClToElTransfersEnabled() | |
1042 | + | if ((checkClToEl == checkClToEl)) | |
1043 | + | then { | |
1044 | + | let checkPayment = if ((size(i.payments) == 1)) | |
1045 | + | then true | |
1046 | + | else throw(("Expected one payment as a transfer amount, got " + toString(size(i.payments)))) | |
1047 | + | if ((checkPayment == checkPayment)) | |
1048 | + | then { | |
1049 | + | let tokenIdStr = getStringValue(tokenIdKey) | |
1050 | + | let tokenId = fromBase58String(tokenIdStr) | |
1051 | + | let t = i.payments[0] | |
1052 | + | let checkAssetId = match t.assetId { | |
1053 | + | case assetId: ByteVector => | |
1054 | + | if ((assetId == tokenId)) | |
1055 | + | then true | |
1056 | + | else throw(((("Expected " + tokenIdStr) + " in the payment, got ") + toBase58String(assetId))) | |
1057 | + | case _ => | |
1058 | + | throw((("Expected " + tokenIdStr) + " in the payment, got Waves")) | |
1059 | + | } | |
1060 | + | if ((checkAssetId == checkAssetId)) | |
1061 | + | then { | |
1062 | + | let lastIndex = blockMeta(mainChainLastBlock)._7 | |
1063 | + | let queueSize = ((nativeTransfersCount - lastIndex) + 1) | |
1064 | + | let checkQueueLimit = if ((160 > queueSize)) | |
1065 | + | then amountGtEq(t, 1000000, queueSize) | |
1066 | + | else if ((1600 > queueSize)) | |
1067 | + | then amountGtEq(t, 10000000, queueSize) | |
1068 | + | else if ((3200 > queueSize)) | |
1069 | + | then amountGtEq(t, 100000000, queueSize) | |
1070 | + | else if ((6400 > queueSize)) | |
1071 | + | then amountGtEq(t, 1000000000, queueSize) | |
1072 | + | else throw((("Transfers denied for queue size of " + toString(queueSize)) + ". Wait until current transfers processed")) | |
1073 | + | if ((checkQueueLimit == checkQueueLimit)) | |
1074 | + | then [IntegerEntry(nativeTransfersCountKey, (nativeTransfersCount + 1)), mkNativeTransferEntry(nativeTransfersCount, destElAddressHex, t.amount), Burn(tokenId, t.amount)] | |
1075 | + | else throw("Strict value is not equal to itself.") | |
1076 | + | } | |
1077 | + | else throw("Strict value is not equal to itself.") | |
1078 | + | } | |
1079 | + | else throw("Strict value is not equal to itself.") | |
1080 | + | } | |
1081 | + | else throw("Strict value is not equal to itself.") | |
966 | 1082 | } | |
967 | 1083 | ||
968 | 1084 | ||
969 | 1085 | ||
970 | 1086 | @Callable(i) | |
971 | 1087 | func withdraw (blockHashHex,merkleProof,transferIndexInBlock,amount) = { | |
972 | - | let checkActivation = requireElToClTransfersEnabled() | |
973 | - | if ((checkActivation == checkActivation)) | |
974 | - | then { | |
975 | - | let withdrawBlockMeta = blockMeta(blockHashHex) | |
976 | - | let withdrawBlockHeight = withdrawBlockMeta._1 | |
977 | - | let finalizedBlockHeight = blockMeta(getStringValue(finalizedBlockKey))._1 | |
978 | - | let mainChainLastBlockHeight = blockMeta(mainChainLastBlock)._1 | |
979 | - | if ((withdrawBlockHeight > finalizedBlockHeight)) | |
980 | - | then throw(((("EL block #" + toString(withdrawBlockHeight)) + " is not finalized. The current finalized is #") + toString(finalizedBlockHeight))) | |
981 | - | else { | |
982 | - | let withdrawBlockChainId = withdrawBlockMeta._5 | |
983 | - | let isMainChain = (withdrawBlockChainId == mainChainId) | |
984 | - | let relatesToMainChain = match getInteger(chainForkedHeightKey(withdrawBlockChainId)) { | |
985 | - | case forkedHeight: Int => | |
986 | - | (forkedHeight > withdrawBlockHeight) | |
987 | - | case _ => | |
988 | - | throw((((blockHashHex + " is on an alternative chain #") + toString(withdrawBlockChainId)) + " that was not approved by majority. Wait for some blocks")) | |
1088 | + | let withdrawBlockMeta = blockMeta(blockHashHex) | |
1089 | + | let withdrawBlockHeight = withdrawBlockMeta._1 | |
1090 | + | let finalizedBlockHeight = blockMeta(getStringValue(finalizedBlockKey))._1 | |
1091 | + | let mainChainLastBlockHeight = blockMeta(mainChainLastBlock)._1 | |
1092 | + | if ((withdrawBlockHeight > finalizedBlockHeight)) | |
1093 | + | then throw(((("EL block #" + toString(withdrawBlockHeight)) + " is not finalized. The current finalized is #") + toString(finalizedBlockHeight))) | |
1094 | + | else { | |
1095 | + | let withdrawBlockChainId = withdrawBlockMeta._5 | |
1096 | + | let isMainChain = (withdrawBlockChainId == mainChainId) | |
1097 | + | let relatesToMainChain = match getInteger(chainForkedHeightKey(withdrawBlockChainId)) { | |
1098 | + | case forkedHeight: Int => | |
1099 | + | (forkedHeight > withdrawBlockHeight) | |
1100 | + | case _ => | |
1101 | + | throw((((blockHashHex + " is on an alternative chain #") + toString(withdrawBlockChainId)) + " that was not approved by majority. Wait for some blocks")) | |
1102 | + | } | |
1103 | + | if (if (isMainChain) | |
1104 | + | then true | |
1105 | + | else relatesToMainChain) | |
1106 | + | then { | |
1107 | + | let recipient = i.originCaller | |
1108 | + | let recipientPkHash = take(drop(recipient.bytes, 2), PUBLIC_KEY_HASH_SIZE) | |
1109 | + | let zeroAmountBytes = base58'11111111111111111111111111111111111111111111' | |
1110 | + | let amountBytes = toBytes(amount) | |
1111 | + | let elEventData = ((recipientPkHash + take(zeroAmountBytes, (size(zeroAmountBytes) - size(amountBytes)))) + amountBytes) | |
1112 | + | let elEventDataDigest = blake2b256_16Kb(elEventData) | |
1113 | + | let calculatedRootHash = createMerkleRoot(merkleProof, elEventDataDigest, transferIndexInBlock) | |
1114 | + | let expectedRootHash = withdrawBlockMeta._6 | |
1115 | + | if ((calculatedRootHash == expectedRootHash)) | |
1116 | + | then { | |
1117 | + | let tokenId = fromBase58String(getStringValue(tokenIdKey)) | |
1118 | + | let transfersKey = blockElToClTransfersKey(blockHashHex) | |
1119 | + | [Reissue(tokenId, amount, true), ScriptTransfer(recipient, amount, tokenId), StringEntry(transfersKey, setOrFail(valueOrElse(getString(transfersKey), ""), transferIndexInBlock))] | |
1120 | + | } | |
1121 | + | else throw((((((("Expected root hash: " + toBase16String(expectedRootHash)) + ", got: ") + toBase16String(calculatedRootHash)) + ". Event data digest: ") + toBase64String(elEventDataDigest)) + ". Check your withdraw arguments")) | |
989 | 1122 | } | |
990 | - | if (if (isMainChain) | |
991 | - | then true | |
992 | - | else relatesToMainChain) | |
993 | - | then { | |
994 | - | let recipient = i.originCaller | |
995 | - | let recipientPkHash = take(drop(recipient.bytes, 2), PUBLIC_KEY_HASH_SIZE) | |
996 | - | let zeroAmountBytes = base58'11111111111111111111111111111111111111111111' | |
997 | - | let amountBytes = toBytes(amount) | |
998 | - | let elEventData = ((recipientPkHash + take(zeroAmountBytes, (size(zeroAmountBytes) - size(amountBytes)))) + amountBytes) | |
999 | - | let elEventDataDigest = blake2b256_16Kb(elEventData) | |
1000 | - | let calculatedRootHash = createMerkleRoot(merkleProof, elEventDataDigest, transferIndexInBlock) | |
1001 | - | let expectedRootHash = withdrawBlockMeta._6 | |
1002 | - | if ((calculatedRootHash == expectedRootHash)) | |
1003 | - | then { | |
1004 | - | let tokenId = fromBase58String(getStringValue(tokenIdKey)) | |
1005 | - | let transfersKey = blockElToClTransfersKey(blockHashHex) | |
1006 | - | [Reissue(tokenId, amount, true), ScriptTransfer(recipient, amount, tokenId), StringEntry(transfersKey, setOrFail(valueOrElse(getString(transfersKey), ""), transferIndexInBlock))] | |
1007 | - | } | |
1008 | - | else throw(((((("Expected root hash: " + toBase16String(expectedRootHash)) + ", got: ") + toBase16String(calculatedRootHash)) + ". Event data digest: ") + toBase64String(elEventDataDigest))) | |
1009 | - | } | |
1010 | - | else throw((("Expected " + blockHashHex) + " to be either on the main chain or relate to it")) | |
1011 | - | } | |
1123 | + | else throw((("Expected " + blockHashHex) + " to be either on the main chain or relate to it")) | |
1012 | 1124 | } | |
1013 | - | else throw("Strict value is not equal to itself.") | |
1014 | 1125 | } | |
1015 | 1126 | ||
1016 | 1127 | ||
1017 | 1128 | ||
1018 | 1129 | @Callable(i) | |
1019 | 1130 | func setup (genesisBlockHashHex,minerRewardInGwei,stakingContractAddressB58,elBridgeAddressHex) = if (isContractSetup()) | |
1020 | 1131 | then throw("The contract has been already set up") | |
1021 | 1132 | else if ((0 > minerRewardInGwei)) | |
1022 | 1133 | then throw("The miner reward must be nonnegative") | |
1023 | 1134 | else { | |
1024 | 1135 | let genesisBlockHash = fromBase16String(genesisBlockHashHex) | |
1025 | 1136 | let emptyPk = base58'11111111111111111111111111111111' | |
1026 | 1137 | let genesisMinerAddress = addressFromPublicKey(emptyPk) | |
1027 | 1138 | let genesisEthRewardAddress = base58'11111111111111111111' | |
1028 | 1139 | let genesisBlockReferenceHash = "0000000000000000000000000000000000000000000000000000000000000000" | |
1029 | 1140 | let issue = Issue("UNIT0", "Native token", 0, 8, true) | |
1030 | 1141 | let tokenId = calculateAssetId(issue) | |
1031 | - | [mkBlockMetaEntry(genesisBlockHashHex, 0, genesisBlockReferenceHash, genesisMinerAddress, mainChainId, ""), StringEntry(chainFirstBlockIdKey(0), genesisBlockHashHex), StringEntry(chainMetaKey(0), ("0," + genesisBlockHashHex)), IntegerEntry(minerRewardKey, minerRewardInGwei), StringEntry(stakingContractAddressKey, stakingContractAddressB58), StringEntry(epochMetaKey(height), ((toString(genesisMinerAddress) + ",0,") + genesisBlockHashHex)), StringEntry(finalizedBlockKey, genesisBlockHashHex), IntegerEntry(prevRandaoHeightKey, 0), IntegerEntry(elToClTransfersEpochKey, 0), issue, StringEntry(tokenIdKey, toBase58String(tokenId)), StringEntry(elBridgeAddressKey, ("0x" + elBridgeAddressHex))] | |
1142 | + | let genesisBlockMeta = ((((((toBytes(0) + toBytes(height)) + fromBase16String(genesisBlockReferenceHash)) + genesisMinerAddress.bytes) + toBytes(0)) + base58'') + toBytes(-1)) | |
1143 | + | [BinaryEntry((blockMetaK + genesisBlockHashHex), genesisBlockMeta), StringEntry(chainFirstBlockIdKey(0), genesisBlockHashHex), mkChainMetaEntry(0, 0, genesisBlockHashHex), IntegerEntry(minerRewardKey, minerRewardInGwei), StringEntry(stakingContractAddressKey, stakingContractAddressB58), StringEntry(epochMetaKey(height), ((toString(genesisMinerAddress) + ",0,") + genesisBlockHashHex)), StringEntry(finalizedBlockKey, genesisBlockHashHex), issue, StringEntry(tokenIdKey, toBase58String(tokenId)), StringEntry(elBridgeAddressKey, ("0x" + elBridgeAddressHex)), IntegerEntry(clToElTransfersEpochKey, 0)] | |
1032 | 1144 | } | |
1033 | - | ||
1034 | - | ||
1035 | - | ||
1036 | - | @Callable(i) | |
1037 | - | func setupElToClTransfers (activationHeight,elBridgeAddressHex) = if (isDefined(getInteger(elToClTransfersEpochKey))) | |
1038 | - | then throw("EL to CL transfers feature was set up") | |
1039 | - | else { | |
1040 | - | let issue = Issue("UNIT0", "Native token", 0, 8, true) | |
1041 | - | let tokenId = calculateAssetId(issue) | |
1042 | - | [IntegerEntry(elToClTransfersEpochKey, activationHeight), issue, StringEntry(tokenIdKey, toBase58String(tokenId)), StringEntry(elBridgeAddressKey, ("0x" + elBridgeAddressHex))] | |
1043 | - | } | |
1044 | 1145 | ||
1045 | 1146 |
github/deemru/w8io/026f985 145.24 ms ◑