tx · 2EWu1PugPUm3LTX33R1oGgkX4qEnsnaJu55LKi6LmoUn

3MsqKJ6o1ABE37676cHHBxJRs6huYTt72ch:  -0.02600000 Waves

2024.05.07 11:07 [3095491] smart account 3MsqKJ6o1ABE37676cHHBxJRs6huYTt72ch > SELF 0.00000000 Waves

{ "type": 13, "id": "2EWu1PugPUm3LTX33R1oGgkX4qEnsnaJu55LKi6LmoUn", "fee": 2600000, "feeAssetId": null, "timestamp": 1715072454000, "version": 1, "sender": "3MsqKJ6o1ABE37676cHHBxJRs6huYTt72ch", "senderPublicKey": "3xjN6fjYDXBGUE1mcRw2Fvr4R6tEZnuJA98QFGF99sXd", "proofs": [ "2nNbiqrach4RZ6mjj61uwATUv6vRtrY91dKsN72mFyrUmqKhV3YMYnVWCcewZToWsGbgSPG3xUGh6Tw1cBakFv5i" ], "script": "base64:CAIsCAISBAoCCAgSBQoDCAgBEgUKAwgIARIGCgQBCAgBEgMKAQISABIFCgMIAQg/AAdJTlRfTUFYAP//////////fwAFV0FWRVMAgMLXLwALTUlOX0JBTEFOQ0UJAGgCAKCcAQUFV0FWRVMAA1NFUAIBLAAPQkxPQ0tfSEFTSF9TSVpFACAADEFERFJFU1NfU0laRQAaABB0aGlzRXBvY2hEYXRhS2V5Ag10aGlzRXBvY2hEYXRhAAxhbGxNaW5lcnNLZXkCCWFsbE1pbmVycwAObWFpbkNoYWluSWRLZXkCC21haW5DaGFpbklkAA5sYXN0Q2hhaW5JZEtleQILbGFzdENoYWluSWQAF2ZpcnN0VmFsaWRBbHRDaGFpbklkS2V5AhRmaXJzdFZhbGlkQWx0Q2hhaW5JZAAObWluZXJSZXdhcmRLZXkCC21pbmVyUmV3YXJkABlzdGFraW5nQ29udHJhY3RBZGRyZXNzS2V5AhZzdGFraW5nQ29udHJhY3RBZGRyZXNzAApibG9ja01ldGFLAghibG9ja18weAEDcGFkAQFpBAFzCQCkAwEFAWkEByRtYXRjaDAJALECAQUBcwMJAAACAAEFByRtYXRjaDAJAKwCAgIHMDAwMDAwMAUBcwMJAAACAAIFByRtYXRjaDAJAKwCAgIGMDAwMDAwBQFzAwkAAAIAAwUHJG1hdGNoMAkArAICAgUwMDAwMAUBcwMJAAACAAQFByRtYXRjaDAJAKwCAgIEMDAwMAUBcwMJAAACAAUFByRtYXRjaDAJAKwCAgIDMDAwBQFzAwkAAAIABgUHJG1hdGNoMAkArAICAgIwMAUBcwMJAAACAAcFByRtYXRjaDAJAKwCAgIBMAUBcwUBcwEMZXBvY2hNZXRhS2V5AQVlcG9jaAkArAICAgZlcG9jaF8JAQNwYWQBBQVlcG9jaAEUY2hhaW5GaXJzdEJsb2NrSWRLZXkBB2NoYWluSWQJAKwCAgkArAICAgVjaGFpbgkApAMBBQdjaGFpbklkAgpGaXJzdEJsb2NrAQxjaGFpbk1ldGFLZXkBB2NoYWluSWQJAKwCAgIGY2hhaW5fCQEDcGFkAQUHY2hhaW5JZAESY2hhaW5MYXN0SGVpZ2h0S2V5AgdjaGFpbklkBW1pbmVyCQCsAgIJAKwCAgkArAICAgZjaGFpbl8JAQNwYWQBBQdjaGFpbklkAgFfCQClCAEFBW1pbmVyAQ1zdXBwb3J0ZXJzS2V5AQdjaGFpbklkCQCsAgIJAKwCAgIFY2hhaW4JAKQDAQUHY2hhaW5JZAIKU3VwcG9ydGVycwEVbWluZXJSZXdhcmRBZGRyZXNzS2V5AQltaW5lckFkZHIJAKwCAgkArAICAgZtaW5lcl8FCW1pbmVyQWRkcgIOX1Jld2FyZEFkZHJlc3MBCm1pbmVyUGtLZXkBDXJld2FyZEFkZHJlc3MJAKwCAgkArAICAghtaW5lcl8weAUNcmV3YXJkQWRkcmVzcwIDX1BLAQ9taW5lckNoYWluSWRLZXkBBW1pbmVyCQCsAgIJAKwCAgIGbWluZXJfCQClCAEFBW1pbmVyAghfQ2hhaW5JZAAWc3Rha2luZ0NvbnRyYWN0QWRkcmVzcwQHJG1hdGNoMAkAnQgCBQR0aGlzBRlzdGFraW5nQ29udHJhY3RBZGRyZXNzS2V5AwkAAQIFByRtYXRjaDACBlN0cmluZwQBcwUHJG1hdGNoMAkBE3ZhbHVlT3JFcnJvck1lc3NhZ2UCCQCmCAEFAXMJAKwCAgIiaW52YWxpZCBzdGFraW5nIGNvbnRyYWN0IGFkZHJlc3M6IAUBcwkBB0FkZHJlc3MBCQERQGV4dHJOYXRpdmUoMTA1MikCBQR0aGlzBRlzdGFraW5nQ29udHJhY3RBZGRyZXNzS2V5ARFnZW5lcmF0aW5nQmFsYW5jZQEHYWRkcmVzcwQHJG1hdGNoMAkAnQgCBRZzdGFraW5nQ29udHJhY3RBZGRyZXNzCQCsAgICBCVzX18JAKUIAQUHYWRkcmVzcwMJAAECBQckbWF0Y2gwAgZTdHJpbmcEA3N0cgUHJG1hdGNoMAQJcGFyYW1MaXN0CQC1CQIFA3N0cgICX18ECnByZXZIZWlnaHQJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQlwYXJhbUxpc3QAAQQLcHJldkJhbGFuY2UJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQlwYXJhbUxpc3QAAgQKbmV4dEhlaWdodAkBDXBhcnNlSW50VmFsdWUBCQCRAwIFCXBhcmFtTGlzdAADBAtuZXh0QmFsYW5jZQkBDXBhcnNlSW50VmFsdWUBCQCRAwIFCXBhcmFtTGlzdAAEAwkAZwIFBmhlaWdodAUKbmV4dEhlaWdodAULbmV4dEJhbGFuY2UDCQBnAgUGaGVpZ2h0BQpwcmV2SGVpZ2h0BQtwcmV2QmFsYW5jZQAAAAABCWNoYWluTWV0YQEHY2hhaW5JZAQBcwkBEUBleHRyTmF0aXZlKDEwNTgpAQkBDGNoYWluTWV0YUtleQEFB2NoYWluSWQEBWl0ZW1zCQC1CQIFAXMFA1NFUAkAlAoCCQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUFaXRlbXMAAAkAkQMCBQVpdGVtcwABAAttYWluQ2hhaW5JZAkBC3ZhbHVlT3JFbHNlAgkAnwgBBQ5tYWluQ2hhaW5JZEtleQAAAAskdDAyNzgwMjg0NgkBCWNoYWluTWV0YQEFC21haW5DaGFpbklkAA9tYWluQ2hhaW5IZWlnaHQIBQskdDAyNzgwMjg0NgJfMQASbWFpbkNoYWluTGFzdEJsb2NrCAULJHQwMjc4MDI4NDYCXzIBCWVwb2NoTWV0YQEFZXBvY2gEByRtYXRjaDAJAKIIAQkBDGVwb2NoTWV0YUtleQEFBWVwb2NoAwkAAQIFByRtYXRjaDACBlN0cmluZwQBcwUHJG1hdGNoMAQJZnJhZ21lbnRzCQC1CQIFAXMFA1NFUAkAlQoDCQERQGV4dHJOYXRpdmUoMTA2MikBCQCRAwIFCWZyYWdtZW50cwAACQENcGFyc2VJbnRWYWx1ZQEJAJEDAgUJZnJhZ21lbnRzAAEJAJEDAgUJZnJhZ21lbnRzAAIFBHVuaXQACyR0MDMwNzgzNTQyBAckbWF0Y2gwCQEJZXBvY2hNZXRhAQUGaGVpZ2h0AwkAAQIFByRtYXRjaDACFihBZGRyZXNzLCBJbnQsIFN0cmluZykEAW0FByRtYXRjaDAFAW0EByRtYXRjaDEJAKIIAQUQdGhpc0Vwb2NoRGF0YUtleQMJAAECBQckbWF0Y2gxAgZTdHJpbmcEEHJhd1RoaXNFcG9jaERhdGEFByRtYXRjaDEEDXRoaXNFcG9jaERhdGEJALUJAgUQcmF3VGhpc0Vwb2NoRGF0YQUDU0VQBAl0aGlzRXBvY2gJAQ1wYXJzZUludFZhbHVlAQkAkQMCBQ10aGlzRXBvY2hEYXRhAAAJAJUKAwMJAAACBQl0aGlzRXBvY2gFBmhlaWdodAkBEUBleHRyTmF0aXZlKDEwNjIpAQkAkQMCBQ10aGlzRXBvY2hEYXRhAAEFBHVuaXQAAAIACQCVCgMFBHVuaXQAAAIAAA50aGlzRXBvY2hNaW5lcggFCyR0MDMwNzgzNTQyAl8xAAx0aGlzRXBvY2hSZWYIBQskdDAzMDc4MzU0MgJfMgASdGhpc0Vwb2NoTGFzdEJsb2NrCAULJHQwMzA3ODM1NDICXzMADGFsbE1pbmVyc1N0cgkBC3ZhbHVlT3JFbHNlAgkAoggBBQxhbGxNaW5lcnNLZXkCAAAJYWxsTWluZXJzBAckbWF0Y2gwBQxhbGxNaW5lcnNTdHIDCQAAAgIABQckbWF0Y2gwBQNuaWwDCQABAgUHJG1hdGNoMAIGU3RyaW5nBANyYXcFByRtYXRjaDAJALwJAgUDcmF3BQNTRVAJAAIBAgtNYXRjaCBlcnJvcgEJYmxvY2tNZXRhAQdibG9ja0lkBARtZXRhCQERQGV4dHJOYXRpdmUoMTA1NykBCQCsAgIFCmJsb2NrTWV0YUsFB2Jsb2NrSWQEC2Jsb2NrSGVpZ2h0CQCxCQEFBG1ldGEECmJsb2NrRXBvY2gJALIJAgUEbWV0YQAIBAtibG9ja1BhcmVudAkAyQECCQDKAQIFBG1ldGEAEAUPQkxPQ0tfSEFTSF9TSVpFBA5ibG9ja0dlbmVyYXRvcgkAzAECBQRtZXRhBQxBRERSRVNTX1NJWkUJAJYKBAULYmxvY2tIZWlnaHQFCmJsb2NrRXBvY2gFC2Jsb2NrUGFyZW50BQ5ibG9ja0dlbmVyYXRvcgEMbGFzdEhlaWdodEJ5AgVtaW5lcgdjaGFpbklkBAckbWF0Y2gwCQCfCAEJARJjaGFpbkxhc3RIZWlnaHRLZXkCBQdjaGFpbklkBQVtaW5lcgMJAAECBQckbWF0Y2gwAgNJbnQEAWgFByRtYXRjaDAFAWgECWJsb2NrSGFzaAkBEUBleHRyTmF0aXZlKDEwNTgpAQkArAICCQCsAgIJAKwCAgIFY2hhaW4JAKQDAQUHY2hhaW5JZAILTGFzdE1pbmVkQnkJAKUIAQUFbWluZXIICQEJYmxvY2tNZXRhAQUJYmxvY2tIYXNoAl8xAAskdDA0MzAyNTI2OAQJaGl0U291cmNlBAckbWF0Y2gwCAUJbGFzdEJsb2NrA3ZyZgMJAAECBQckbWF0Y2gwAgpCeXRlVmVjdG9yBAN2cmYFByRtYXRjaDAFA3ZyZggFCWxhc3RCbG9jaxNnZW5lcmF0aW9uU2lnbmF0dXJlCgEMcHJvY2Vzc01pbmVyAgRwcmV2BW1pbmVyBAskdDA0NjAwNDY2MwUEcHJldgQJcHJldkRlbGF5CAULJHQwNDYwMDQ2NjMCXzEECXByZXZNaW5lcggFCyR0MDQ2MDA0NjYzAl8yBBBwcmV2VG90YWxCYWxhbmNlCAULJHQwNDYwMDQ2NjMCXzMECnByZXZNaW5lcnMIBQskdDA0NjAwNDY2MwJfNAQMbWluZXJBZGRyZXNzCQERQGV4dHJOYXRpdmUoMTA2MikBBQVtaW5lcgQPd2F2ZXNHZW5CYWxhbmNlCAkA7wcBBQxtaW5lckFkZHJlc3MKZ2VuZXJhdGluZwQMbWluZXJCYWxhbmNlCQERZ2VuZXJhdGluZ0JhbGFuY2UBBQxtaW5lckFkZHJlc3MDAwkAZgIFC01JTl9CQUxBTkNFBQ93YXZlc0dlbkJhbGFuY2UGCQBnAgAABQxtaW5lckJhbGFuY2UFBHByZXYECW5leHREZWxheQkAhQcCBQxtaW5lckFkZHJlc3MFDG1pbmVyQmFsYW5jZQMJAGYCBQlwcmV2RGVsYXkFCW5leHREZWxheQkAlgoEBQluZXh0RGVsYXkFBW1pbmVyCQBkAgUQcHJldlRvdGFsQmFsYW5jZQUMbWluZXJCYWxhbmNlCQDNCAIFCnByZXZNaW5lcnMFBW1pbmVyCQCWCgQFCXByZXZEZWxheQUJcHJldk1pbmVyCQBkAgUQcHJldlRvdGFsQmFsYW5jZQUMbWluZXJCYWxhbmNlCQDNCAIFCnByZXZNaW5lcnMFBW1pbmVyCgACJGwFCWFsbE1pbmVycwoAAiRzCQCQAwEFAiRsCgAFJGFjYzAJAJYKBAUHSU5UX01BWAIAAAAFA25pbAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEMcHJvY2Vzc01pbmVyAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhRMaXN0IHNpemUgZXhjZWVkcyA1MAkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIADWNvbXB1dGVkRGVsYXkIBQskdDA0MzAyNTI2OAJfMQARY29tcHV0ZWRHZW5lcmF0b3IIBQskdDA0MzAyNTI2OAJfMgAUY29tcHV0ZWRUb3RhbEJhbGFuY2UIBQskdDA0MzAyNTI2OAJfMwAOZmlsdGVyZWRNaW5lcnMIBQskdDA0MzAyNTI2OAJfNAETZ2V0Q2hhaW5MYXN0QmxvY2tJZAEHY2hhaW5JZAgJAQljaGFpbk1ldGEBBQdjaGFpbklkAl8yAAskdDA1MzM0NTQzNgkBCWJsb2NrTWV0YQEFEm1haW5DaGFpbkxhc3RCbG9jawAHaWdub3JlZAgFCyR0MDUzMzQ1NDM2Al8xAA5tYWluQ2hhaW5FcG9jaAgFCyR0MDUzMzQ1NDM2Al8yABNtYWluQ2hhaW5QYXJlbnRIYXNoCAULJHQwNTMzNDU0MzYCXzMAEm1haW5DaGFpbkdlbmVyYXRvcggFCyR0MDUzMzQ1NDM2Al80AAskdDA1NDM4NjU1NAQLb2Zmc2V0c18yMDAJALUJAgLIATo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6AgAEC2hhbGZCYWxhbmNlCQBpAgUUY29tcHV0ZWRUb3RhbEJhbGFuY2UAAgoBBHN0ZXACBHByZXYEbmV4dAQLJHQwNTg1MTU5MTUFBHByZXYECXRoaXNFcG9jaAgFCyR0MDU4NTE1OTE1Al8xBAx0b3RhbEJhbGFuY2UIBQskdDA1ODUxNTkxNQJfMgQObWF5YmVTYWZlRXBvY2gIBQskdDA1ODUxNTkxNQJfMwQKcHJldk1pbmVycwgFCyR0MDU4NTE1OTE1Al80BAckbWF0Y2gwBQ5tYXliZVNhZmVFcG9jaAMJAAECBQckbWF0Y2gwAgRVbml0BAskdDA1OTczNjA0MQkBBXZhbHVlAQkBCWVwb2NoTWV0YQEFCXRoaXNFcG9jaAQFbWluZXIIBQskdDA1OTczNjA0MQJfMQQJcHJldkVwb2NoCAULJHQwNTk3MzYwNDECXzIEDWxhc3RCbG9ja0hhc2gIBQskdDA1OTczNjA0MQJfMwQLJHQwNjA1MDYyNDYDCQEPY29udGFpbnNFbGVtZW50AgUKcHJldk1pbmVycwUFbWluZXIJAJQKAgUMdG90YWxCYWxhbmNlBQpwcmV2TWluZXJzCQCUCgIJAGQCBQx0b3RhbEJhbGFuY2UJARFnZW5lcmF0aW5nQmFsYW5jZQEFBW1pbmVyCQDMCAIFBW1pbmVyBQpwcmV2TWluZXJzBA9uZXdUb3RhbEJhbGFuY2UIBQskdDA2MDUwNjI0NgJfMQQJbmV3TWluZXJzCAULJHQwNjA1MDYyNDYCXzIDCQBnAgUPbmV3VG90YWxCYWxhbmNlBQtoYWxmQmFsYW5jZQkAlgoEBQl0aGlzRXBvY2gFD25ld1RvdGFsQmFsYW5jZQUJdGhpc0Vwb2NoBQlhbGxNaW5lcnMJAJYKBAUJcHJldkVwb2NoBQ9uZXdUb3RhbEJhbGFuY2UFBHVuaXQFCW5ld01pbmVycwUEcHJldgQDcmVzCgACJGwFC29mZnNldHNfMjAwCgACJHMJAJADAQUCJGwKAAUkYWNjMAkAlgoEBQ5tYWluQ2hhaW5FcG9jaAAABQR1bml0BQNuaWwKAQUkZjBfMQICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkBBHN0ZXACBQIkYQkAkQMCBQIkbAUCJGkKAQUkZjBfMgICJGECJGkDCQBnAgUCJGkFAiRzBQIkYQkAAgECFUxpc3Qgc2l6ZSBleGNlZWRzIDIwMAkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIAMwA0ADUANgA3ADgAOQA6ADsAPAA9AD4APwBAAEEAQgBDAEQARQBGAEcASABJAEoASwBMAE0ATgBPAFAAUQBSAFMAVABVAFYAVwBYAFkAWgBbAFwAXQBeAF8AYABhAGIAYwBkAGUAZgBnAGgAaQBqAGsAbABtAG4AbwBwAHEAcgBzAHQAdQB2AHcAeAB5AHoAewB8AH0AfgB/AIABAIEBAIIBAIMBAIQBAIUBAIYBAIcBAIgBAIkBAIoBAIsBAIwBAI0BAI4BAI8BAJABAJEBAJIBAJMBAJQBAJUBAJYBAJcBAJgBAJkBAJoBAJsBAJwBAJ0BAJ4BAJ8BAKABAKEBAKIBAKMBAKQBAKUBAKYBAKcBAKgBAKkBAKoBAKsBAKwBAK0BAK4BAK8BALABALEBALIBALMBALQBALUBALYBALcBALgBALkBALoBALsBALwBAL0BAL4BAL8BAMABAMEBAMIBAMMBAMQBAMUBAMYBAMcBAMgBCQELdmFsdWVPckVsc2UCCAUDcmVzAl8zCQCUCgIAAQIAAA5maW5hbGl6ZWRFcG9jaAgFCyR0MDU0Mzg2NTU0Al8xABJmaW5hbGl6ZWRCbG9ja0hhc2gIBQskdDA1NDM4NjU1NAJfMgERc3VwcG9ydGluZ0JhbGFuY2UBB2NoYWluSWQKAQphZGRCYWxhbmNlAgNhY2MMZ2VuZXJhdG9yU3RyBAskdDA2NjcyNjcwOAUDYWNjBAx0b3RhbEJhbGFuY2UIBQskdDA2NjcyNjcwOAJfMQQKZ2VuZXJhdG9ycwgFCyR0MDY2NzI2NzA4Al8yBAlnZW5lcmF0b3IJARFAZXh0ck5hdGl2ZSgxMDYyKQEFDGdlbmVyYXRvclN0cgMJAQ9jb250YWluc0VsZW1lbnQCBQpnZW5lcmF0b3JzBQlnZW5lcmF0b3IFA2FjYwQHYmFsYW5jZQkBEWdlbmVyYXRpbmdCYWxhbmNlAQUJZ2VuZXJhdG9yCQCUCgIJAGQCBQx0b3RhbEJhbGFuY2UFB2JhbGFuY2UJAM0IAgUKZ2VuZXJhdG9ycwUJZ2VuZXJhdG9yBA1hbGxHZW5lcmF0b3JzCQC8CQIJARFAZXh0ck5hdGl2ZSgxMDU4KQEJAQ1zdXBwb3J0ZXJzS2V5AQUHY2hhaW5JZAUDU0VQBAskdDA3MDMwNzA5NQoAAiRsBQ1hbGxHZW5lcmF0b3JzCgACJHMJAJADAQUCJGwKAAUkYWNjMAkAlAoCAAAFA25pbAoBBSRmMF8xAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQEKYWRkQmFsYW5jZQIFAiRhCQCRAwIFAiRsBQIkaQoBBSRmMF8yAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQACAQIVTGlzdCBzaXplIGV4Y2VlZHMgMTAwCQEFJGYwXzICCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECCQEFJGYwXzECBQUkYWNjMAAAAAEAAgADAAQABQAGAAcACAAJAAoACwAMAA0ADgAPABAAEQASABMAFAAVABYAFwAYABkAGgAbABwAHQAeAB8AIAAhACIAIwAkACUAJgAnACgAKQAqACsALAAtAC4ALwAwADEAMgAzADQANQA2ADcAOAA5ADoAOwA8AD0APgA/AEAAQQBCAEMARABFAEYARwBIAEkASgBLAEwATQBOAE8AUABRAFIAUwBUAFUAVgBXAFgAWQBaAFsAXABdAF4AXwBgAGEAYgBjAGQEB2JhbGFuY2UIBQskdDA3MDMwNzA5NQJfMQQCX2cIBQskdDA3MDMwNzA5NQJfMgUHYmFsYW5jZQEPaXNDb250cmFjdFNldHVwAAkBCWlzRGVmaW5lZAEJAJ8IAQUObWluZXJSZXdhcmRLZXkBEWVuc3VyZU1pbmluZ0Vwb2NoAQlnZW5lcmF0b3IDCQECIT0CCQClCAEFCWdlbmVyYXRvcgURY29tcHV0ZWRHZW5lcmF0b3IJAAIBCQCsAgIJAKwCAgkArAICCQCsAgIJANgEAQgFCWdlbmVyYXRvcgVieXRlcwIbIGlzIG5vdCBhbGxvd2VkIHRvIG1pbmUgaW4gCQCkAwEFBmhlaWdodAIRIGVwb2NoLiBFeHBlY3RlZCAFEWNvbXB1dGVkR2VuZXJhdG9yBQR1bml0ARJpc1JlZmVyZW5jZUNvcnJlY3QCCXJlZmVyZW5jZQlsYXN0QmxvY2sDCQAAAgUJcmVmZXJlbmNlBQlsYXN0QmxvY2sFBHVuaXQJAAIBCQCsAgIJAKwCAgkArAICAjBFeHBlY3RlZCBhIHJlZmVyZW5jZSB0byB0aGUgY2hhaW4gbGFzdCBibG9jazogMHgFCWxhc3RCbG9jawIJLiBHb3Q6IDB4BQlyZWZlcmVuY2UBD2NoYWluSXNJbmFjdGl2ZQEHY2hhaW5JZAQMZmlyc3RCbG9ja0lkCQERQGV4dHJOYXRpdmUoMTA1NykBCQEUY2hhaW5GaXJzdEJsb2NrSWRLZXkBBQdjaGFpbklkBBRmaXJzdFZhbGlkQWx0Q2hhaW5JZAkBC3ZhbHVlT3JFbHNlAgkAnwgBBRdmaXJzdFZhbGlkQWx0Q2hhaW5JZEtleQAAAwkAZgIFFGZpcnN0VmFsaWRBbHRDaGFpbklkBQdjaGFpbklkBgkAZgIFDmZpbmFsaXplZEVwb2NoCAkBCWJsb2NrTWV0YQEJANwEAQUMZmlyc3RCbG9ja0lkAl8xAQxtaW5lckNoYWluSWQBBW1pbmVyCQELdmFsdWVPckVsc2UCCQCfCAEJAQ9taW5lckNoYWluSWRLZXkBBQVtaW5lcgkAnwgBCQCsAgICCWNoYWluSWRPZgkApQgBBQVtaW5lcgEdZW5zdXJlRXhwZWN0ZWRPckluYWN0aXZlQ2hhaW4DCWdlbmVyYXRvcg9leHBlY3RlZENoYWluSWQQY2hlY2tIZWlnaHRCbG9jawQPaGVpZ2h0SXNDb3JyZWN0BAckbWF0Y2gwBRBjaGVja0hlaWdodEJsb2NrAwkAAQIFByRtYXRjaDACBlN0cmluZwQJYmxvY2tIYXNoBQckbWF0Y2gwBBRsYXN0TWluZWRCbG9ja0hlaWdodAkBDGxhc3RIZWlnaHRCeQIFCWdlbmVyYXRvcgULbWFpbkNoYWluSWQJAGYCCQBkAggJAQlibG9ja01ldGEBBQlibG9ja0hhc2gCXzEAAQUUbGFzdE1pbmVkQmxvY2tIZWlnaHQGBAckbWF0Y2gwCQEMbWluZXJDaGFpbklkAQUJZ2VuZXJhdG9yAwkAAQIFByRtYXRjaDACA0ludAQJY3VycmVudElkBQckbWF0Y2gwAwMJAAACBQljdXJyZW50SWQFD2V4cGVjdGVkQ2hhaW5JZAYDCQEPY2hhaW5Jc0luYWN0aXZlAQUJY3VycmVudElkBQ9oZWlnaHRJc0NvcnJlY3QHBQR1bml0CQACAQkArAICAhxtaW5lciBpcyBtaW5pbmcgb3RoZXIgY2hhaW4gCQCkAwEFCWN1cnJlbnRJZAUEdW5pdAESZW5zdXJlQ29ycmVjdEVwb2NoAQVlcG9jaAMJAAACBQVlcG9jaAUGaGVpZ2h0BQR1bml0CQACAQkArAICCQCsAgIJAKwCAgIaRXhwZWN0ZWQgYmxvY2sgZnJvbSBlcG9jaCAJAKQDAQUGaGVpZ2h0AgYuIEdvdCAJAKQDAQUFZXBvY2gBDGFkZFN1cHBvcnRlcgIHY2hhaW5JZAlnZW5lcmF0b3IEDXN1cHBvcnRlcnNTdHIJARFAZXh0ck5hdGl2ZSgxMDU4KQEJAQ1zdXBwb3J0ZXJzS2V5AQUHY2hhaW5JZAQKc3VwcG9ydGVycwkAtQkCBQ1zdXBwb3J0ZXJzU3RyBQNTRVADCQEPY29udGFpbnNFbGVtZW50AgUKc3VwcG9ydGVycwkApQgBBQlnZW5lcmF0b3IFA25pbAkAzAgCCQELU3RyaW5nRW50cnkCCQENc3VwcG9ydGVyc0tleQEFB2NoYWluSWQJAKwCAgkArAICBQ1zdXBwb3J0ZXJzU3RyBQNTRVAJAKUIAQUJZ2VuZXJhdG9yBQNuaWwBEXZhbGlkYXRlQmxvY2tIYXNoAQZoZXhTdHIEDGRlY29kZWRCeXRlcwkA3QQBBQZoZXhTdHIDCQECIT0CCQDIAQEFDGRlY29kZWRCeXRlcwAgCQACAQIXaW52YWxpZCBibG9jayBpZCBsZW5ndGgFBmhleFN0cgcBaQELYXBwZW5kQmxvY2sCDGJsb2NrSGFzaEhleAxyZWZlcmVuY2VIZXgDCQECIT0CBQ50aGlzRXBvY2hNaW5lcggFAWkMb3JpZ2luQ2FsbGVyBAckbWF0Y2gwBQ50aGlzRXBvY2hNaW5lcgMJAAECBQckbWF0Y2gwAgdBZGRyZXNzBAplcG9jaE1pbmVyBQckbWF0Y2gwCQACAQkArAICAjlub3QgYWxsb3dlZCB0byBmb3JnZSBibG9ja3MgaW4gdGhpcyBlcG9jaCwgZXhwZWN0ZWQgZnJvbSAJAKUIAQUKZXBvY2hNaW5lcgkAAgECQG5vdCBhbGxvd2VkIHRvIGZvcmdlIGJsb2NrcyBpbiB0aGlzIGVwb2NoLCBlcG9jaCBtaW5lciBpcyBhYnNlbnQEB2NoYWluSWQJAQt2YWx1ZU9yRWxzZQIJAQxtaW5lckNoYWluSWQBCAUBaQxvcmlnaW5DYWxsZXIFC21haW5DaGFpbklkBAskdDA5ODUwOTkwMQkBCWNoYWluTWV0YQEFB2NoYWluSWQEC2NoYWluSGVpZ2h0CAULJHQwOTg1MDk5MDECXzEEC2xhc3RCbG9ja0lkCAULJHQwOTg1MDk5MDECXzIEDmNoZWNrUmVmZXJlbmNlCQESaXNSZWZlcmVuY2VDb3JyZWN0AgUMcmVmZXJlbmNlSGV4BQtsYXN0QmxvY2tJZAMJAAACBQ5jaGVja1JlZmVyZW5jZQUOY2hlY2tSZWZlcmVuY2UEDm5ld0NoYWluSGVpZ2h0CQBkAgULY2hhaW5IZWlnaHQAAQQMbmV3QmxvY2tNZXRhCQDLAQIJAMsBAgkAywECCQCaAwEFDm5ld0NoYWluSGVpZ2h0CQCaAwEFBmhlaWdodAkA3QQBBQtsYXN0QmxvY2tJZAgIBQFpDG9yaWdpbkNhbGxlcgVieXRlcwQJYmxvY2tIYXNoCQERdmFsaWRhdGVCbG9ja0hhc2gBBQxibG9ja0hhc2hIZXgJAMwIAgkBC0JpbmFyeUVudHJ5AgkArAICBQpibG9ja01ldGFLBQxibG9ja0hhc2hIZXgFDG5ld0Jsb2NrTWV0YQkAzAgCCQEMSW50ZWdlckVudHJ5AgkBEmNoYWluTGFzdEhlaWdodEtleQIFB2NoYWluSWQIBQFpDG9yaWdpbkNhbGxlcgUObmV3Q2hhaW5IZWlnaHQJAMwIAgkBC1N0cmluZ0VudHJ5AgkBDGNoYWluTWV0YUtleQEFB2NoYWluSWQJAKwCAgkArAICCQCkAwEFDm5ld0NoYWluSGVpZ2h0BQNTRVAFDGJsb2NrSGFzaEhleAkAzAgCCQELU3RyaW5nRW50cnkCCQEMZXBvY2hNZXRhS2V5AQUGaGVpZ2h0CQCsAgIJAKwCAgkArAICCQCsAgIJAKUIAQkBBXZhbHVlAQUOdGhpc0Vwb2NoTWluZXIFA1NFUAkApAMBBQx0aGlzRXBvY2hSZWYFA1NFUAUMYmxvY2tIYXNoSGV4BQNuaWwJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4BaQEPZXh0ZW5kTWFpbkNoYWluAwxibG9ja0hhc2hIZXgMcmVmZXJlbmNlSGV4BWVwb2NoBApjaGVja0Vwb2NoCQESZW5zdXJlQ29ycmVjdEVwb2NoAQUFZXBvY2gDCQAAAgUKY2hlY2tFcG9jaAUKY2hlY2tFcG9jaAQOY2hlY2tHZW5lcmF0b3IJARFlbnN1cmVNaW5pbmdFcG9jaAEIBQFpDG9yaWdpbkNhbGxlcgMJAAACBQ5jaGVja0dlbmVyYXRvcgUOY2hlY2tHZW5lcmF0b3IECmNoZWNrQ2hhaW4JAR1lbnN1cmVFeHBlY3RlZE9ySW5hY3RpdmVDaGFpbgMIBQFpDG9yaWdpbkNhbGxlcgULbWFpbkNoYWluSWQFBHVuaXQDCQAAAgUKY2hlY2tDaGFpbgUKY2hlY2tDaGFpbgQOY2hlY2tSZWZlcmVuY2UJARJpc1JlZmVyZW5jZUNvcnJlY3QCBQxyZWZlcmVuY2VIZXgFEm1haW5DaGFpbkxhc3RCbG9jawMJAAACBQ5jaGVja1JlZmVyZW5jZQUOY2hlY2tSZWZlcmVuY2UEDXRoaXNFcG9jaE1ldGEEByRtYXRjaDAJAQllcG9jaE1ldGEBBQZoZWlnaHQDCQABAgUHJG1hdGNoMAIEVW5pdAkBC1N0cmluZ0VudHJ5AgkBDGVwb2NoTWV0YUtleQEFBmhlaWdodAkArAICCQCsAgIJAKwCAgkArAICCQClCAEIBQFpDG9yaWdpbkNhbGxlcgUDU0VQCQCkAwEFDm1haW5DaGFpbkVwb2NoBQNTRVAFDGJsb2NrSGFzaEhleAQFb3RoZXIFByRtYXRjaDAJAAIBAhVFcG9jaCBhbHJlYWR5IHN0YXJ0ZWQEDm5ld0NoYWluSGVpZ2h0CQBkAgUPbWFpbkNoYWluSGVpZ2h0AAEEDG5ld0Jsb2NrTWV0YQkAywECCQDLAQIJAMsBAgkAmgMBBQ5uZXdDaGFpbkhlaWdodAkAmgMBBQZoZWlnaHQJAN0EAQUSbWFpbkNoYWluTGFzdEJsb2NrCAgFAWkMb3JpZ2luQ2FsbGVyBWJ5dGVzCQDMCAIJAQtCaW5hcnlFbnRyeQIJAKwCAgUKYmxvY2tNZXRhSwUMYmxvY2tIYXNoSGV4BQxuZXdCbG9ja01ldGEJAMwIAgkBC1N0cmluZ0VudHJ5AgkBDGNoYWluTWV0YUtleQEFC21haW5DaGFpbklkCQCsAgIJAKwCAgkApAMBBQ5uZXdDaGFpbkhlaWdodAUDU0VQBQxibG9ja0hhc2hIZXgJAMwIAgkBDEludGVnZXJFbnRyeQIJAQ9taW5lckNoYWluSWRLZXkBCAUBaQxvcmlnaW5DYWxsZXIFC21haW5DaGFpbklkCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQESY2hhaW5MYXN0SGVpZ2h0S2V5AgULbWFpbkNoYWluSWQIBQFpDG9yaWdpbkNhbGxlcgUObmV3Q2hhaW5IZWlnaHQJAMwIAgUNdGhpc0Vwb2NoTWV0YQUDbmlsCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuCQACAQIkU3RyaWN0IHZhbHVlIGlzIG5vdCBlcXVhbCB0byBpdHNlbGYuAWkBDXN0YXJ0QWx0Q2hhaW4DDGJsb2NrSGFzaEhleAxyZWZlcmVuY2VIZXgFZXBvY2gECmNoZWNrRXBvY2gJARJlbnN1cmVDb3JyZWN0RXBvY2gBBQVlcG9jaAMJAAACBQpjaGVja0Vwb2NoBQpjaGVja0Vwb2NoBA5jaGVja0dlbmVyYXRvcgkBEWVuc3VyZU1pbmluZ0Vwb2NoAQgFAWkMb3JpZ2luQ2FsbGVyAwkAAAIFDmNoZWNrR2VuZXJhdG9yBQ5jaGVja0dlbmVyYXRvcgQNJHQwMTE5ODExMjA1OQkBCWJsb2NrTWV0YQEFDHJlZmVyZW5jZUhleAQOcmVmQ2hhaW5IZWlnaHQIBQ0kdDAxMTk4MTEyMDU5Al8xBAhyZWZFcG9jaAgFDSR0MDExOTgxMTIwNTkCXzIEBnJlZlJlZggFDSR0MDExOTgxMTIwNTkCXzMEDHJlZkdlbmVyYXRvcggFDSR0MDExOTgxMTIwNTkCXzQECGVwb2NoUmVmAwkAZwIFCHJlZkVwb2NoBQ5maW5hbGl6ZWRFcG9jaAUIcmVmRXBvY2gJAAIBCQCsAgIJAKwCAgkArAICCQCsAgICI2NhbiBub3Qgc3RhcnQgYWx0IGNoYWluIGZyb20gZXBvY2ggCQCkAwEFCHJlZkVwb2NoAggsIGVwb2NoIAkApAMBBQ5maW5hbGl6ZWRFcG9jaAINIGlzIGZpbmFsaXplZAQKY2hlY2tDaGFpbgkBHWVuc3VyZUV4cGVjdGVkT3JJbmFjdGl2ZUNoYWluAwgFAWkMb3JpZ2luQ2FsbGVyBQttYWluQ2hhaW5JZAUMcmVmZXJlbmNlSGV4AwkAAAIFCmNoZWNrQ2hhaW4FCmNoZWNrQ2hhaW4ECm5ld0NoYWluSWQJAGQCCQELdmFsdWVPckVsc2UCCQCfCAEFDmxhc3RDaGFpbklkS2V5AAAAAQQPcmVmZXJlbmNlSGVpZ2h0CAkBCWJsb2NrTWV0YQEFDHJlZmVyZW5jZUhleAJfMQQObmV3Q2hhaW5IZWlnaHQJAGQCBQ9yZWZlcmVuY2VIZWlnaHQAAQQMbmV3QmxvY2tNZXRhCQDLAQIJAMsBAgkAywECCQCaAwEFDm5ld0NoYWluSGVpZ2h0CQCaAwEFBmhlaWdodAkA3QQBBQxyZWZlcmVuY2VIZXgICAUBaQxvcmlnaW5DYWxsZXIFYnl0ZXMEDXRoaXNFcG9jaE1ldGEEByRtYXRjaDAJAQllcG9jaE1ldGEBBQZoZWlnaHQDCQABAgUHJG1hdGNoMAIEVW5pdAkBC1N0cmluZ0VudHJ5AgkBDGVwb2NoTWV0YUtleQEFBmhlaWdodAkArAICCQCsAgIJAKwCAgkArAICCQClCAEIBQFpDG9yaWdpbkNhbGxlcgUDU0VQCQCkAwEFCGVwb2NoUmVmBQNTRVAFDGJsb2NrSGFzaEhleAQFb3RoZXIFByRtYXRjaDAJAAIBAhVFcG9jaCBhbHJlYWR5IHN0YXJ0ZWQJAMwIAgUNdGhpc0Vwb2NoTWV0YQkAzAgCCQELQmluYXJ5RW50cnkCCQCsAgIFCmJsb2NrTWV0YUsFDGJsb2NrSGFzaEhleAUMbmV3QmxvY2tNZXRhCQDMCAIJAQtTdHJpbmdFbnRyeQIJARRjaGFpbkZpcnN0QmxvY2tJZEtleQEFCm5ld0NoYWluSWQFDGJsb2NrSGFzaEhleAkAzAgCCQELU3RyaW5nRW50cnkCCQEMY2hhaW5NZXRhS2V5AQULbWFpbkNoYWluSWQJAKwCAgkArAICCQCkAwEFDm5ld0NoYWluSGVpZ2h0BQNTRVAFDGJsb2NrSGFzaEhleAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBD21pbmVyQ2hhaW5JZEtleQEIBQFpDG9yaWdpbkNhbGxlcgUKbmV3Q2hhaW5JZAkAzAgCCQEMSW50ZWdlckVudHJ5AgkBEmNoYWluTGFzdEhlaWdodEtleQIFCm5ld0NoYWluSWQIBQFpDG9yaWdpbkNhbGxlcgUObmV3Q2hhaW5IZWlnaHQJAMwIAgkBDEludGVnZXJFbnRyeQIJARJjaGFpbkxhc3RIZWlnaHRLZXkCBQttYWluQ2hhaW5JZAgFAWkMb3JpZ2luQ2FsbGVyBQ9yZWZlcmVuY2VIZWlnaHQJAMwIAgkBC1N0cmluZ0VudHJ5AgkBDXN1cHBvcnRlcnNLZXkBBQpuZXdDaGFpbklkCQClCAEIBQFpDG9yaWdpbkNhbGxlcgkAzAgCCQEMSW50ZWdlckVudHJ5AgUObGFzdENoYWluSWRLZXkFCm5ld0NoYWluSWQFA25pbAkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgkAAgECJFN0cmljdCB2YWx1ZSBpcyBub3QgZXF1YWwgdG8gaXRzZWxmLgFpAQ5leHRlbmRBbHRDaGFpbgQHY2hhaW5JZAxibG9ja0hhc2hIZXgMcmVmZXJlbmNlSGV4BWVwb2NoBApjaGVja0Vwb2NoCQESZW5zdXJlQ29ycmVjdEVwb2NoAQUFZXBvY2gDCQAAAgUKY2hlY2tFcG9jaAUKY2hlY2tFcG9jaAQOY2hlY2tHZW5lcmF0b3IJARFlbnN1cmVNaW5pbmdFcG9jaAEIBQFpDG9yaWdpbkNhbGxlcgMJAAACBQ5jaGVja0dlbmVyYXRvcgUOY2hlY2tHZW5lcmF0b3IECmNoZWNrQ2hhaW4JAR1lbnN1cmVFeHBlY3RlZE9ySW5hY3RpdmVDaGFpbgMIBQFpDG9yaWdpbkNhbGxlcgUHY2hhaW5JZAkA3AQBCAkBCWJsb2NrTWV0YQEJARFAZXh0ck5hdGl2ZSgxMDU4KQEJARRjaGFpbkZpcnN0QmxvY2tJZEtleQEFB2NoYWluSWQCXzMDCQAAAgUKY2hlY2tDaGFpbgUKY2hlY2tDaGFpbgQNJHQwMTQxNTkxNDIxMwkBCWNoYWluTWV0YQEFB2NoYWluSWQEC2NoYWluSGVpZ2h0CAUNJHQwMTQxNTkxNDIxMwJfMQQOY2hhaW5MYXN0QmxvY2sIBQ0kdDAxNDE1OTE0MjEzAl8yBA5jaGVja1JlZmVyZW5jZQkBEmlzUmVmZXJlbmNlQ29ycmVjdAIFDHJlZmVyZW5jZUhleAUOY2hhaW5MYXN0QmxvY2sDCQAAAgUOY2hlY2tSZWZlcmVuY2UFDmNoZWNrUmVmZXJlbmNlBA5uZXdDaGFpbkhlaWdodAkAZAIFC2NoYWluSGVpZ2h0AAEEDm1haW5DaGFpbkVudHJ5AwkAZgIJARFzdXBwb3J0aW5nQmFsYW5jZQEFB2NoYWluSWQJAGkCBRRjb21wdXRlZFRvdGFsQmFsYW5jZQACCQDMCAIJAQxJbnRlZ2VyRW50cnkCBQ5tYWluQ2hhaW5JZEtleQUHY2hhaW5JZAkAzAgCCQEMSW50ZWdlckVudHJ5AgUXZmlyc3RWYWxpZEFsdENoYWluSWRLZXkJAGQCCQELdmFsdWVPckVsc2UCCQCfCAEFDmxhc3RDaGFpbklkS2V5AAAAAQUDbmlsBQNuaWwEDXRoaXNFcG9jaE1ldGEEByRtYXRjaDAJAQllcG9jaE1ldGEBBQZoZWlnaHQDCQABAgUHJG1hdGNoMAIEVW5pdAkBC1N0cmluZ0VudHJ5AgkBDGVwb2NoTWV0YUtleQEFBmhlaWdodAkArAICCQCsAgIJAKwCAgkArAICCQClCAEIBQFpDG9yaWdpbkNhbGxlcgUDU0VQCQCkAwEICQEJYmxvY2tNZXRhAQUMcmVmZXJlbmNlSGV4Al8yBQNTRVAFDGJsb2NrSGFzaEhleAQFb3RoZXIFByRtYXRjaDAJAAIBAhVFcG9jaCBhbHJlYWR5IHN0YXJ0ZWQEHXVwZGF0ZU1haW5DaGFpbkxhc3RNaW5lZEJsb2NrAwMJAAACBQ5tYWluQ2hhaW5FbnRyeQUDbmlsCQECIT0CCQELdmFsdWVPckVsc2UCCQEMbWluZXJDaGFpbklkAQgFAWkMb3JpZ2luQ2FsbGVyAAAFB2NoYWluSWQHCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQESY2hhaW5MYXN0SGVpZ2h0S2V5AgULbWFpbkNoYWluSWQIBQFpDG9yaWdpbkNhbGxlcggJAQlibG9ja01ldGEBCQERQGV4dHJOYXRpdmUoMTA1OCkBCQEUY2hhaW5GaXJzdEJsb2NrSWRLZXkBBQdjaGFpbklkAl8xBQNuaWwFA25pbAQMbmV3QmxvY2tNZXRhCQDLAQIJAMsBAgkAywECCQCaAwEFDm5ld0NoYWluSGVpZ2h0CQCaAwEFBmhlaWdodAkA3QQBBQxyZWZlcmVuY2VIZXgICAUBaQxvcmlnaW5DYWxsZXIFYnl0ZXMJAM4IAgkAzggCCQDOCAIJAMwIAgkBC0JpbmFyeUVudHJ5AgkArAICBQpibG9ja01ldGFLBQxibG9ja0hhc2hIZXgFDG5ld0Jsb2NrTWV0YQkAzAgCCQELU3RyaW5nRW50cnkCCQEMY2hhaW5NZXRhS2V5AQULbWFpbkNoYWluSWQJAKwCAgkArAICCQCkAwEFDm5ld0NoYWluSGVpZ2h0BQNTRVAFDGJsb2NrSGFzaEhleAkAzAgCBQ10aGlzRXBvY2hNZXRhCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQEPbWluZXJDaGFpbklkS2V5AQgFAWkMb3JpZ2luQ2FsbGVyBQdjaGFpbklkCQDMCAIJAQxJbnRlZ2VyRW50cnkCCQESY2hhaW5MYXN0SGVpZ2h0S2V5AgUHY2hhaW5JZAgFAWkMb3JpZ2luQ2FsbGVyBQ5uZXdDaGFpbkhlaWdodAUDbmlsBQ5tYWluQ2hhaW5FbnRyeQkBDGFkZFN1cHBvcnRlcgIFB2NoYWluSWQIBQFpDG9yaWdpbkNhbGxlcgUddXBkYXRlTWFpbkNoYWluTGFzdE1pbmVkQmxvY2sJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4JAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4BaQEEam9pbgENcmV3YXJkQWRkcmVzcwoBEmNoZWNrUmV3YXJkQWRkcmVzcwEHYWRkcmVzcwQHJG1hdGNoMAkAoQgBCQEKbWluZXJQa0tleQEFB2FkZHJlc3MDCQABAgUHJG1hdGNoMAIKQnl0ZVZlY3RvcgQCcGsFByRtYXRjaDADCQAAAgUCcGsIBQFpFW9yaWdpbkNhbGxlclB1YmxpY0tleQUEdW5pdAkAAgEJAKwCAgkArAICCQCsAgICEUwyIG1pbmVyIGFkZHJlc3MgBQdhZGRyZXNzAhggaXMgYWxyZWFkeSBsaW5rZWQgd2l0aCAJANgEAQUCcGsFBHVuaXQDCQEBIQEJAQ9pc0NvbnRyYWN0U2V0dXAACQACAQIfVGhlIGNvbnRyYWN0IGhhcyBub3QgeWV0IHNldCB1cAMJAGYCBQtNSU5fQkFMQU5DRQgJAO8HAQgFAWkMb3JpZ2luQ2FsbGVyCmdlbmVyYXRpbmcJAAIBCQCsAgIJAKwCAgkArAICAiFJbnN1ZmZpY2llbnQgZ2VuZXJhdGluZyBiYWxhbmNlOiAJAKQDAQgJAO8HAQgFAWkMb3JpZ2luQ2FsbGVyCmdlbmVyYXRpbmcCDC4gUmVxdWlyZWQ6IAkApAMBBQtNSU5fQkFMQU5DRQMJAQIhPQIJAMgBAQUNcmV3YXJkQWRkcmVzcwAUCQACAQIlcmV3YXJkQWRkcmVzcyBzaG91bGQgYmUgYW4gTDIgYWRkcmVzcwoBDmNoZWNrRXhpc3RlbmNlAgZleGlzdHMFbWluZXIDBQZleGlzdHMGCQAAAgUFbWluZXIJAKUIAQgFAWkMb3JpZ2luQ2FsbGVyBA1hbHJlYWR5RXhpc3RzCgACJGwFCWFsbE1pbmVycwoAAiRzCQCQAwEFAiRsCgAFJGFjYzAHCgEFJGYwXzECAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAQ5jaGVja0V4aXN0ZW5jZQIFAiRhCQCRAwIFAiRsBQIkaQoBBSRmMF8yAgIkYQIkaQMJAGcCBQIkaQUCJHMFAiRhCQACAQIUTGlzdCBzaXplIGV4Y2VlZHMgNTAJAQUkZjBfMgIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIJAQUkZjBfMQIFBSRhY2MwAAAAAQACAAMABAAFAAYABwAIAAkACgALAAwADQAOAA8AEAARABIAEwAUABUAFgAXABgAGQAaABsAHAAdAB4AHwAgACEAIgAjACQAJQAmACcAKAApACoAKwAsAC0ALgAvADAAMQAyAwUNYWxyZWFkeUV4aXN0cwUDbmlsBAhuZXdNaW5lcgkApQgBCAUBaQxvcmlnaW5DYWxsZXIEEHJld2FyZEFkZHJlc3NIZXgJANwEAQUNcmV3YXJkQWRkcmVzcwQFY2hlY2sJARJjaGVja1Jld2FyZEFkZHJlc3MBBRByZXdhcmRBZGRyZXNzSGV4AwkAAAIFBWNoZWNrBQVjaGVjawQJbmV3TWluZXJzAwkAAAIJAJADAQUJYWxsTWluZXJzAAAFCG5ld01pbmVyCQCsAgIJAKwCAgUMYWxsTWluZXJzU3RyBQNTRVAFCG5ld01pbmVyBBhkZWxldGVPbGRSZXdhcmRBZGRyZXNzUGsEByRtYXRjaDAJAKEIAQkBFW1pbmVyUmV3YXJkQWRkcmVzc0tleQEFCG5ld01pbmVyAwkAAQIFByRtYXRjaDACCkJ5dGVWZWN0b3IECm9sZEFkZHJlc3MFByRtYXRjaDADCQAAAgUKb2xkQWRkcmVzcwUNcmV3YXJkQWRkcmVzcwUDbmlsCQDMCAIJAQtEZWxldGVFbnRyeQEJAQptaW5lclBrS2V5AQkA3AQBBQpvbGRBZGRyZXNzBQNuaWwFA25pbAkAzggCCQDMCAIJAQtTdHJpbmdFbnRyeQIFDGFsbE1pbmVyc0tleQUJbmV3TWluZXJzCQDMCAIJAQtTdHJpbmdFbnRyeQIJARVtaW5lclJld2FyZEFkZHJlc3NLZXkBBQhuZXdNaW5lcgkArAICAgIweAUQcmV3YXJkQWRkcmVzc0hleAkAzAgCCQELQmluYXJ5RW50cnkCCQEKbWluZXJQa0tleQEFEHJld2FyZEFkZHJlc3NIZXgIBQFpFW9yaWdpbkNhbGxlclB1YmxpY0tleQUDbmlsBRhkZWxldGVPbGRSZXdhcmRBZGRyZXNzUGsJAAIBAiRTdHJpY3QgdmFsdWUgaXMgbm90IGVxdWFsIHRvIGl0c2VsZi4BaQEFbGVhdmUABAxsZWF2aW5nTWluZXIJAKUIAQgFAWkMb3JpZ2luQ2FsbGVyCgEQc2tpcExlYXZpbmdNaW5lcgIDYWNjBW1pbmVyAwkAAAIFBW1pbmVyBQxsZWF2aW5nTWluZXIFA2FjYwkAzQgCBQNhY2MFBW1pbmVyBA9yZW1haW5pbmdNaW5lcnMKAAIkbAUJYWxsTWluZXJzCgACJHMJAJADAQUCJGwKAAUkYWNjMAUDbmlsCgEFJGYwXzECAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJARBza2lwTGVhdmluZ01pbmVyAgUCJGEJAJEDAgUCJGwFAiRpCgEFJGYwXzICAiRhAiRpAwkAZwIFAiRpBQIkcwUCJGEJAAIBAhRMaXN0IHNpemUgZXhjZWVkcyA1MAkBBSRmMF8yAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgkBBSRmMF8xAgUFJGFjYzAAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAIQAiACMAJAAlACYAJwAoACkAKgArACwALQAuAC8AMAAxADIEDXJld2FyZEFkZHJLZXkJARVtaW5lclJld2FyZEFkZHJlc3NLZXkBBQxsZWF2aW5nTWluZXIEEXByZXZSZXdhcmRBZGRyZXNzCQETdmFsdWVPckVycm9yTWVzc2FnZQIJAJwIAgUEdGhpcwUNcmV3YXJkQWRkcktleQIWbWluZXIgaGFzIG5ldmVyIGpvaW5lZAMJAAACBQ50aGlzRXBvY2hNaW5lcggFAWkMb3JpZ2luQ2FsbGVyCQACAQIcZGVzaWduYXRlZCBtaW5lciBjYW4ndCBsZWF2ZQkAzAgCCQELU3RyaW5nRW50cnkCBQxhbGxNaW5lcnNLZXkJALoJAgUPcmVtYWluaW5nTWluZXJzBQNTRVAJAMwIAgkBC0RlbGV0ZUVudHJ5AQUNcmV3YXJkQWRkcktleQkAzAgCCQELRGVsZXRlRW50cnkBCQEKbWluZXJQa0tleQEJANwEAQURcHJldlJld2FyZEFkZHJlc3MFA25pbAFpAQVzZXR1cAMTZ2VuZXNpc0Jsb2NrSGFzaEhleAttaW5lclJld2FyZBlzdGFraW5nQ29udHJhY3RBZGRyZXNzQjU4AwkBD2lzQ29udHJhY3RTZXR1cAAJAAIBAiRUaGUgY29udHJhY3QgaGFzIGJlZW4gYWxyZWFkeSBzZXQgdXAEEGdlbmVzaXNCbG9ja0hhc2gJAN0EAQUTZ2VuZXNpc0Jsb2NrSGFzaEhleAQHZW1wdHlQawEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE2dlbmVzaXNNaW5lckFkZHJlc3MJAKcIAQUHZW1wdHlQawQXZ2VuZXNpc0V0aFJld2FyZEFkZHJlc3MBFAAAAAAAAAAAAAAAAAAAAAAAAAAABBlnZW5lc2lzQmxvY2tSZWZlcmVuY2VIYXNoASAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQQZ2VuZXNpc0Jsb2NrTWV0YQkAywECCQDLAQIJAMsBAgkAmgMBAAAJAJoDAQUGaGVpZ2h0BRlnZW5lc2lzQmxvY2tSZWZlcmVuY2VIYXNoCAUTZ2VuZXNpc01pbmVyQWRkcmVzcwVieXRlcwkAzAgCCQELQmluYXJ5RW50cnkCCQCsAgIFCmJsb2NrTWV0YUsFE2dlbmVzaXNCbG9ja0hhc2hIZXgFEGdlbmVzaXNCbG9ja01ldGEJAMwIAgkBC0JpbmFyeUVudHJ5AgkBFGNoYWluRmlyc3RCbG9ja0lkS2V5AQAABRBnZW5lc2lzQmxvY2tIYXNoCQDMCAIJAQtTdHJpbmdFbnRyeQIJAQxjaGFpbk1ldGFLZXkBAAAJAKwCAgICMCwFE2dlbmVzaXNCbG9ja0hhc2hIZXgJAMwIAgkBDEludGVnZXJFbnRyeQIFDm1pbmVyUmV3YXJkS2V5BQttaW5lclJld2FyZAkAzAgCCQELU3RyaW5nRW50cnkCBRlzdGFraW5nQ29udHJhY3RBZGRyZXNzS2V5BRlzdGFraW5nQ29udHJhY3RBZGRyZXNzQjU4CQDMCAIJAQtTdHJpbmdFbnRyeQIJAQxlcG9jaE1ldGFLZXkBBQZoZWlnaHQJAKwCAgkArAICCQClCAEFE2dlbmVzaXNNaW5lckFkZHJlc3MCAywwLAUTZ2VuZXNpc0Jsb2NrSGFzaEhleAUDbmlsAHI99C0=", "chainId": 84, "height": 3095491, "applicationStatus": "succeeded", "spentComplexity": 0 } View: original | compacted Prev: n6cwhqFoj5W2LDqTeQBXhskSaZsYjiPmX6BiU1YrYc3 Next: 4WWwz1Kg5sx8vVdrC6ivyizGEngERutep2rFvUA3eQr2 Diff:
OldNewDifferences
77
88 let MIN_BALANCE = (20000 * WAVES)
99
10-let MAX_BLOCKS_AT_EPOCH = 50
11-
1210 let SEP = ","
1311
1412 let BLOCK_HASH_SIZE = 32
1513
1614 let ADDRESS_SIZE = 26
17-
18-let MAX_BLOCKS_STR_SIZE = ((64 * MAX_BLOCKS_AT_EPOCH) + (MAX_BLOCKS_AT_EPOCH - 1))
1915
2016 let thisEpochDataKey = "thisEpochData"
2117
2521
2622 let lastChainIdKey = "lastChainId"
2723
28-let lastEpochBlocksNumberKey = "lastEpochBlocksNumber"
24+let firstValidAltChainIdKey = "firstValidAltChainId"
2925
3026 let minerRewardKey = "minerReward"
27+
28+let stakingContractAddressKey = "stakingContractAddress"
3129
3230 let blockMetaK = "block_0x"
3331
5755 func epochMetaKey (epoch) = ("epoch_" + pad(epoch))
5856
5957
58+func chainFirstBlockIdKey (chainId) = (("chain" + toString(chainId)) + "FirstBlock")
59+
60+
6061 func chainMetaKey (chainId) = ("chain_" + pad(chainId))
6162
6263
63-let stakingContractAddress = Address(getBinaryValue(this, "stakingContractAddress"))
64+func chainLastHeightKey (chainId,miner) = ((("chain_" + pad(chainId)) + "_") + toString(miner))
65+
66+
67+func supportersKey (chainId) = (("chain" + toString(chainId)) + "Supporters")
68+
69+
70+func minerRewardAddressKey (minerAddr) = (("miner_" + minerAddr) + "_RewardAddress")
71+
72+
73+func minerPkKey (rewardAddress) = (("miner_0x" + rewardAddress) + "_PK")
74+
75+
76+func minerChainIdKey (miner) = (("miner_" + toString(miner)) + "_ChainId")
77+
78+
79+let stakingContractAddress = match getString(this, stakingContractAddressKey) {
80+ case s: String =>
81+ valueOrErrorMessage(addressFromString(s), ("invalid staking contract address: " + s))
82+ case _ =>
83+ Address(getBinaryValue(this, stakingContractAddressKey))
84+}
6485
6586 func generatingBalance (address) = match getString(stakingContractAddress, ("%s__" + toString(address))) {
6687 case str: String =>
79100 }
80101
81102
82-func blockMetaKey (blockId) = ("blockMeta" + toBase16String(blockId))
83-
84-
85-func chainIdKey (generator) = ("chainIdOf" + toBase16String(generator))
86-
87-
88-func chainHeightKey (chainId) = (("chain" + toString(chainId)) + "Height")
89-
90-
91-func chainFirstBlockIdKey (chainId) = (("chain" + toString(chainId)) + "FirstBlock")
92-
93-
94-func chainLastBlockIdKey (chainId) = (("chain" + toString(chainId)) + "LastBlock")
95-
96-
97-func chainAllBlockIdsKey (chainId,n) = ((("chain" + toString(chainId)) + "AllBlocks") + toString(n))
98-
99-
100-func chainAllBlockIdsLastNKey (chainId) = (("chain" + toString(chainId)) + "AllBlocksLastN")
101-
102-
103-func supportersKey (chainId) = (("chain" + toString(chainId)) + "Supporters")
104-
105-
106-func minerRewardAddress (minerAddr) = (("miner" + minerAddr) + "RewardAddress")
107-
108-
109-func minerJoinHeightKey (minerAddr) = (("miner" + minerAddr) + "JoinHeight")
110-
111-
112-func minerPkKey (rewardAddress) = (("miner" + rewardAddress) + "PK")
113-
114-
115-func leavingMinersKey (epoch) = ("leavingMinersAt" + toString(epoch))
103+func chainMeta (chainId) = {
104+ let s = getStringValue(chainMetaKey(chainId))
105+ let items = split(s, SEP)
106+ $Tuple2(parseIntValue(items[0]), items[1])
107+ }
116108
117109
118110 let mainChainId = valueOrElse(getInteger(mainChainIdKey), 0)
119111
120-let thisEpochMiner = match getString(thisEpochDataKey) {
121- case rawThisEpochData: String =>
122- let thisEpochData = split(rawThisEpochData, SEP)
123- let thisEpoch = parseIntValue(thisEpochData[0])
124- if ((thisEpoch == height))
125- then fromBase58String(thisEpochData[1])
126- else unit
112+let $t027802846 = chainMeta(mainChainId)
113+
114+let mainChainHeight = $t027802846._1
115+
116+let mainChainLastBlock = $t027802846._2
117+
118+func epochMeta (epoch) = match getString(epochMetaKey(epoch)) {
119+ case s: String =>
120+ let fragments = split(s, SEP)
121+ $Tuple3(addressFromStringValue(fragments[0]), parseIntValue(fragments[1]), fragments[2])
127122 case _ =>
128123 unit
129124 }
130125
131-let allMiners = match getString(allMinersKey) {
132- case raw: String =>
133- split_4C(raw, SEP)
126+
127+let $t030783542 = match epochMeta(height) {
128+ case m: (Address, Int, String) =>
129+ m
134130 case _ =>
135- nil
131+ match getString(thisEpochDataKey) {
132+ case rawThisEpochData: String =>
133+ let thisEpochData = split(rawThisEpochData, SEP)
134+ let thisEpoch = parseIntValue(thisEpochData[0])
135+ $Tuple3(if ((thisEpoch == height))
136+ then addressFromStringValue(thisEpochData[1])
137+ else unit, 0, "")
138+ case _ =>
139+ $Tuple3(unit, 0, "")
140+ }
136141 }
137142
138-let allLeavingMiners = match getString(leavingMinersKey(height)) {
139- case raw: String =>
140- split_4C(raw, SEP)
143+let thisEpochMiner = $t030783542._1
144+
145+let thisEpochRef = $t030783542._2
146+
147+let thisEpochLastBlock = $t030783542._3
148+
149+let allMinersStr = valueOrElse(getString(allMinersKey), "")
150+
151+let allMiners = match allMinersStr {
141152 case _ =>
142- nil
153+ if (("" == $match0))
154+ then nil
155+ else if ($isInstanceOf($match0, "String"))
156+ then {
157+ let raw = $match0
158+ split_4C(raw, SEP)
159+ }
160+ else throw("Match error")
143161 }
144162
145163 func blockMeta (blockId) = {
146- let meta = getBinaryValue(blockMetaKey(blockId))
147- let metaWithoutHeight = drop(meta, 8)
148- let metaWithoutHeightEpoch = drop(metaWithoutHeight, 8)
149- let blockHeight = toInt(take(meta, 8))
150- let blockEpoch = toInt(take(metaWithoutHeight, 8))
151- let blockParent = take(metaWithoutHeightEpoch, BLOCK_HASH_SIZE)
164+ let meta = getBinaryValue((blockMetaK + blockId))
165+ let blockHeight = toInt(meta)
166+ let blockEpoch = toInt(meta, 8)
167+ let blockParent = take(drop(meta, 16), BLOCK_HASH_SIZE)
152168 let blockGenerator = takeRight(meta, ADDRESS_SIZE)
153169 $Tuple4(blockHeight, blockEpoch, blockParent, blockGenerator)
154170 }
155171
156172
157-func setEpochData (generator) = StringEntry(thisEpochDataKey, ((toString(height) + SEP) + toBase58String(generator.bytes)))
173+func lastHeightBy (miner,chainId) = match getInteger(chainLastHeightKey(chainId, miner)) {
174+ case h: Int =>
175+ h
176+ case _ =>
177+ let blockHash = getStringValue(((("chain" + toString(chainId)) + "LastMinedBy") + toString(miner)))
178+ blockMeta(blockHash)._1
179+}
158180
159181
160-let $t041315188 = {
182+let $t043025268 = {
161183 let hitSource = match lastBlock.vrf {
162184 case vrf: ByteVector =>
163185 vrf
165187 lastBlock.generationSignature
166188 }
167189 func processMiner (prev,miner) = {
168- let $t044294492 = prev
169- let prevDelay = $t044294492._1
170- let prevMiner = $t044294492._2
171- let prevTotalBalance = $t044294492._3
172- let prevMiners = $t044294492._4
190+ let $t046004663 = prev
191+ let prevDelay = $t046004663._1
192+ let prevMiner = $t046004663._2
193+ let prevTotalBalance = $t046004663._3
194+ let prevMiners = $t046004663._4
173195 let minerAddress = addressFromStringValue(miner)
174196 let wavesGenBalance = wavesBalance(minerAddress).generating
175197 let minerBalance = generatingBalance(minerAddress)
176- if (if (if ((MIN_BALANCE > wavesGenBalance))
198+ if (if ((MIN_BALANCE > wavesGenBalance))
177199 then true
178200 else (0 >= minerBalance))
179- then true
180- else (valueOrElse(getInteger(minerJoinHeightKey(miner)), INT_MAX) > height))
181201 then prev
182202 else {
183203 let nextDelay = calculateDelay(minerAddress, minerBalance)
187207 }
188208 }
189209
190- let $l = (allMiners ++ allLeavingMiners)
210+ let $l = allMiners
191211 let $s = size($l)
192212 let $acc0 = $Tuple4(INT_MAX, "", 0, nil)
193213 func $f0_1 ($a,$i) = if (($i >= $s))
201221 $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)
202222 }
203223
204-let computedDelay = $t041315188._1
224+let computedDelay = $t043025268._1
205225
206-let computedGenerator = $t041315188._2
226+let computedGenerator = $t043025268._2
207227
208-let computedTotalBalance = $t041315188._3
228+let computedTotalBalance = $t043025268._3
209229
210-let filteredMiners = $t041315188._4
230+let filteredMiners = $t043025268._4
211231
212-let safeRollbackHeight = {
213- func addBalance (acc,blockIdStr) = {
214- let $t053025346 = acc
215- let totalBalance = $t053025346._1
216- let prevId = $t053025346._2
217- let generators = $t053025346._3
218- if ((totalBalance > (computedTotalBalance / 2)))
219- then acc
220- else {
221- let blockId = fromBase16String(blockIdStr)
222- let generator = Address(blockMeta(blockId)._4)
223- if (containsElement(generators, generator))
224- then acc
225- else {
226- let balance = generatingBalance(generator)
227- $Tuple3((totalBalance + balance), blockId, (generators :+ generator))
228- }
229- }
232+func getChainLastBlockId (chainId) = chainMeta(chainId)._2
233+
234+
235+let $t053345436 = blockMeta(mainChainLastBlock)
236+
237+let ignored = $t053345436._1
238+
239+let mainChainEpoch = $t053345436._2
240+
241+let mainChainParentHash = $t053345436._3
242+
243+let mainChainGenerator = $t053345436._4
244+
245+let $t054386554 = {
246+ let offsets_200 = split("::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::", "")
247+ let halfBalance = (computedTotalBalance / 2)
248+ func step (prev,next) = {
249+ let $t058515915 = prev
250+ let thisEpoch = $t058515915._1
251+ let totalBalance = $t058515915._2
252+ let maybeSafeEpoch = $t058515915._3
253+ let prevMiners = $t058515915._4
254+ match maybeSafeEpoch {
255+ case _: Unit =>
256+ let $t059736041 = value(epochMeta(thisEpoch))
257+ let miner = $t059736041._1
258+ let prevEpoch = $t059736041._2
259+ let lastBlockHash = $t059736041._3
260+ let $t060506246 = if (containsElement(prevMiners, miner))
261+ then $Tuple2(totalBalance, prevMiners)
262+ else $Tuple2((totalBalance + generatingBalance(miner)), miner :: prevMiners)
263+ let newTotalBalance = $t060506246._1
264+ let newMiners = $t060506246._2
265+ if ((newTotalBalance >= halfBalance))
266+ then $Tuple4(thisEpoch, newTotalBalance, thisEpoch, allMiners)
267+ else $Tuple4(prevEpoch, newTotalBalance, unit, newMiners)
268+ case _ =>
269+ prev
270+ }
230271 }
231272
232- func getBlockIds (n) = {
233- let rawStr = valueOrElse(getString(chainAllBlockIdsKey(mainChainId, n)), "")
234- if ((rawStr == ""))
235- then nil
236- else split_4C(rawStr, SEP)
237- }
238-
239- let lastN = valueOrElse(getInteger(chainAllBlockIdsLastNKey(mainChainId)), 0)
240- let allBlocksIds = (((getBlockIds(lastN) ++ getBlockIds((lastN - 1))) ++ getBlockIds((lastN - 2))) ++ getBlockIds((lastN - 3)))
241- let $t061146188 = {
242- let $l = allBlocksIds
273+ let res = {
274+ let $l = offsets_200
243275 let $s = size($l)
244- let $acc0 = $Tuple3(0, base58'', nil)
276+ let $acc0 = $Tuple4(mainChainEpoch, 0, unit, nil)
245277 func $f0_1 ($a,$i) = if (($i >= $s))
246278 then $a
247- else addBalance($a, $l[$i])
279+ else step($a, $l[$i])
248280
249281 func $f0_2 ($a,$i) = if (($i >= $s))
250282 then $a
252284
253285 $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)
254286 }
255- let _b = $t061146188._1
256- let blockId = $t061146188._2
257- blockMeta(blockId)._1
287+ valueOrElse(res._3, $Tuple2(1, ""))
258288 }
289+
290+let finalizedEpoch = $t054386554._1
291+
292+let finalizedBlockHash = $t054386554._2
259293
260294 func supportingBalance (chainId) = {
261295 func addBalance (acc,generatorStr) = {
262- let $t063336369 = acc
263- let totalBalance = $t063336369._1
264- let generators = $t063336369._2
296+ let $t066726708 = acc
297+ let totalBalance = $t066726708._1
298+ let generators = $t066726708._2
265299 let generator = addressFromStringValue(generatorStr)
266300 if (containsElement(generators, generator))
267301 then acc
272306 }
273307
274308 let allGenerators = split_4C(getStringValue(supportersKey(chainId)), SEP)
275- let $t066916756 = {
309+ let $t070307095 = {
276310 let $l = allGenerators
277311 let $s = size($l)
278312 let $acc0 = $Tuple2(0, nil)
286320
287321 $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)
288322 }
289- let balance = $t066916756._1
290- let _g = $t066916756._2
323+ let balance = $t070307095._1
324+ let _g = $t070307095._2
291325 balance
292326 }
327+
328+
329+func isContractSetup () = isDefined(getInteger(minerRewardKey))
293330
294331
295332 func ensureMiningEpoch (generator) = if ((toString(generator) != computedGenerator))
297334 else unit
298335
299336
300-func isReferenceCorrect (reference,lastBlock) = match lastBlock {
301- case _: Unit =>
302- unit
303- case lb: ByteVector =>
304- if ((reference == lb))
305- then unit
306- else throw(((("Expected a reference to the chain last block: " + toBase16String(lb)) + ". Got: ") + toBase16String(reference)))
307- case _ =>
308- throw("Match error")
309-}
337+func isReferenceCorrect (reference,lastBlock) = if ((reference == lastBlock))
338+ then unit
339+ else throw(((("Expected a reference to the chain last block: 0x" + lastBlock) + ". Got: 0x") + reference))
310340
311341
312342 func chainIsInactive (chainId) = {
313343 let firstBlockId = getBinaryValue(chainFirstBlockIdKey(chainId))
314- (safeRollbackHeight > blockMeta(firstBlockId)._1)
344+ let firstValidAltChainId = valueOrElse(getInteger(firstValidAltChainIdKey), 0)
345+ if ((firstValidAltChainId > chainId))
346+ then true
347+ else (finalizedEpoch > blockMeta(toBase16String(firstBlockId))._1)
315348 }
316349
317350
318-func ensureMainOrInactiveChain (generator) = match getInteger(chainIdKey(generator.bytes)) {
319- case currentId: Int =>
320- if (if ((currentId == mainChainId))
321- then true
322- else chainIsInactive(currentId))
323- then unit
324- else throw(("miner is mining other chain " + toString(currentId)))
325- case _ =>
326- unit
327-}
351+func minerChainId (miner) = valueOrElse(getInteger(minerChainIdKey(miner)), getInteger(("chainIdOf" + toString(miner))))
328352
329353
330-func ensureAltChainExtension (generator,chainId) = match getInteger(chainIdKey(generator.bytes)) {
331- case currentId: Int =>
332- if ((currentId == chainId))
333- then unit
334- else {
335- let extendingFirstBlockId = getBinaryValue(chainFirstBlockIdKey(chainId))
336- let minerCurrentFirstBlockId = getBinaryValue(chainFirstBlockIdKey(currentId))
337- let extendingFirstBlockHeight = blockMeta(extendingFirstBlockId)._1
338- let minerCurrentFirstBlockHeight = blockMeta(minerCurrentFirstBlockId)._1
339- if ((extendingFirstBlockHeight > minerCurrentFirstBlockHeight))
340- then unit
341- else throw(((((("miner is mining other chain " + toString(currentId)) + ", height of the alt chain first block ") + toString(extendingFirstBlockHeight)) + " is not less than height of the miner active chain first block ") + toString(minerCurrentFirstBlockHeight)))
342- }
343- case _ =>
344- unit
345-}
346-
347-
348-func ensureReferenceIsSafeForRollback (reference) = {
349- let startHeight = blockMeta(reference)._1
350- if ((safeRollbackHeight > startHeight))
351- then throw(((("start height " + toString(startHeight)) + " is less than minimum height ") + toString(safeRollbackHeight)))
352- else unit
354+func ensureExpectedOrInactiveChain (generator,expectedChainId,checkHeightBlock) = {
355+ let heightIsCorrect = match checkHeightBlock {
356+ case blockHash: String =>
357+ let lastMinedBlockHeight = lastHeightBy(generator, mainChainId)
358+ ((blockMeta(blockHash)._1 + 1) > lastMinedBlockHeight)
359+ case _ =>
360+ true
361+ }
362+ match minerChainId(generator) {
363+ case currentId: Int =>
364+ if (if ((currentId == expectedChainId))
365+ then true
366+ else if (chainIsInactive(currentId))
367+ then heightIsCorrect
368+ else false)
369+ then unit
370+ else throw(("miner is mining other chain " + toString(currentId)))
371+ case _ =>
372+ unit
373+ }
353374 }
354375
355376
356377 func ensureCorrectEpoch (epoch) = if ((epoch == height))
357378 then unit
358379 else throw(((("Expected block from epoch " + toString(height)) + ". Got ") + toString(epoch)))
359-
360-
361-func checkBlocksNumber () = if ((MAX_BLOCKS_AT_EPOCH > getIntegerValue(lastEpochBlocksNumberKey)))
362- then unit
363- else throw("Max blocks number at epoch reached")
364380
365381
366382 func addSupporter (chainId,generator) = {
372388 }
373389
374390
391+func validateBlockHash (hexStr) = {
392+ let decodedBytes = fromBase16String(hexStr)
393+ if ((size(decodedBytes) != 32))
394+ then throw("invalid block id length")
395+ else hexStr
396+ }
397+
398+
375399 @Callable(i)
376-func appendBlock (blockHash,reference) = {
377- let check = checkBlocksNumber()
378- if ((check == check))
379- then if ((thisEpochMiner != i.originCaller.bytes))
380- then match thisEpochMiner {
381- case epochMiner: ByteVector =>
382- throw(("not allowed to forge blocks in this epoch, expected from " + toBase58String(epochMiner)))
383- case _ =>
384- throw("not allowed to forge blocks in this epoch, epoch miner is absent")
385- }
386- else {
387- let chainId = valueOrElse(getInteger(chainIdKey(i.originCaller.bytes)), mainChainId)
388- let lastBlockId = getBinaryValue(chainLastBlockIdKey(chainId))
389- let check2 = isReferenceCorrect(reference, lastBlockId)
390- if ((check2 == check2))
391- then {
392- let chainHeight = valueOrElse(getInteger(chainHeightKey(chainId)), 0)
393- let newHeight = (chainHeight + 1)
394- let blocksNumber = valueOrElse(getInteger(lastEpochBlocksNumberKey), 0)
395- let newBlockMeta = (((toBytes(newHeight) + toBytes(height)) + lastBlockId) + i.originCaller.bytes)
396-[BinaryEntry(blockMetaKey(blockHash), newBlockMeta), BinaryEntry((blockMetaK + toBase16String(blockHash)), newBlockMeta), IntegerEntry(chainHeightKey(chainId), newHeight), BinaryEntry(chainLastBlockIdKey(chainId), blockHash), StringEntry(chainMetaKey(chainId), ((toString(newHeight) + SEP) + toBase16String(blockHash))), IntegerEntry(lastEpochBlocksNumberKey, (blocksNumber + 1)), StringEntry(epochMetaKey(height), ((toString(i.originCaller) + ",0,") + toBase16String(blockHash)))]
397- }
398- else throw("Strict value is not equal to itself.")
400+func appendBlock (blockHashHex,referenceHex) = if ((thisEpochMiner != i.originCaller))
401+ then match thisEpochMiner {
402+ case epochMiner: Address =>
403+ throw(("not allowed to forge blocks in this epoch, expected from " + toString(epochMiner)))
404+ case _ =>
405+ throw("not allowed to forge blocks in this epoch, epoch miner is absent")
406+ }
407+ else {
408+ let chainId = valueOrElse(minerChainId(i.originCaller), mainChainId)
409+ let $t098509901 = chainMeta(chainId)
410+ let chainHeight = $t098509901._1
411+ let lastBlockId = $t098509901._2
412+ let checkReference = isReferenceCorrect(referenceHex, lastBlockId)
413+ if ((checkReference == checkReference))
414+ then {
415+ let newChainHeight = (chainHeight + 1)
416+ let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(lastBlockId)) + i.originCaller.bytes)
417+ let blockHash = validateBlockHash(blockHashHex)
418+[BinaryEntry((blockMetaK + blockHashHex), newBlockMeta), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight), StringEntry(chainMetaKey(chainId), ((toString(newChainHeight) + SEP) + blockHashHex)), StringEntry(epochMetaKey(height), ((((toString(value(thisEpochMiner)) + SEP) + toString(thisEpochRef)) + SEP) + blockHashHex))]
399419 }
400- else throw("Strict value is not equal to itself.")
401- }
420+ else throw("Strict value is not equal to itself.")
421+ }
402422
403423
404424
405425 @Callable(i)
406-func extendMainChain (blockHash,reference,epoch) = {
426+func extendMainChain (blockHashHex,referenceHex,epoch) = {
407427 let checkEpoch = ensureCorrectEpoch(epoch)
408428 if ((checkEpoch == checkEpoch))
409429 then {
410430 let checkGenerator = ensureMiningEpoch(i.originCaller)
411431 if ((checkGenerator == checkGenerator))
412432 then {
413- let checkChain = ensureMainOrInactiveChain(i.originCaller)
433+ let checkChain = ensureExpectedOrInactiveChain(i.originCaller, mainChainId, unit)
414434 if ((checkChain == checkChain))
415435 then {
416- let chainHeight = valueOrElse(getInteger(chainHeightKey(mainChainId)), 0)
417- let lastValidBlockId = getBinary(chainLastBlockIdKey(mainChainId))
418- let checkReference = isReferenceCorrect(reference, lastValidBlockId)
436+ let checkReference = isReferenceCorrect(referenceHex, mainChainLastBlock)
419437 if ((checkReference == checkReference))
420438 then {
421- let blockIdsLastN = valueOrElse(getInteger(chainAllBlockIdsLastNKey(mainChainId)), 0)
422- let blockIds = valueOrElse(getString(chainAllBlockIdsKey(mainChainId, blockIdsLastN)), "")
423- let newHeight = (chainHeight + 1)
424- let updateAllBlockIds = if ((size(blockIds) >= MAX_BLOCKS_STR_SIZE))
425- then [IntegerEntry(chainAllBlockIdsLastNKey(mainChainId), (blockIdsLastN + 1)), StringEntry(chainAllBlockIdsKey(mainChainId, (blockIdsLastN + 1)), toBase16String(blockHash))]
426- else {
427- let newBlockIds = if ((blockIds == ""))
428- then toBase16String(blockHash)
429- else ((toBase16String(blockHash) + SEP) + blockIds)
430-[StringEntry(chainAllBlockIdsKey(mainChainId, blockIdsLastN), newBlockIds)]
431- }
432- let firstBlockId = match lastValidBlockId {
433- case id: ByteVector =>
434- nil
435- case _ =>
436-[BinaryEntry(chainFirstBlockIdKey(mainChainId), blockHash)]
439+ let thisEpochMeta = match epochMeta(height) {
440+ case _: Unit =>
441+ StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(mainChainEpoch)) + SEP) + blockHashHex))
442+ case other =>
443+ throw("Epoch already started")
437444 }
438- let newBlockMeta = (((toBytes(newHeight) + toBytes(height)) + reference) + i.originCaller.bytes)
439- (([BinaryEntry(blockMetaKey(blockHash), newBlockMeta), BinaryEntry((blockMetaK + toBase16String(blockHash)), newBlockMeta), IntegerEntry(chainHeightKey(mainChainId), newHeight), BinaryEntry(chainLastBlockIdKey(mainChainId), blockHash), StringEntry(chainMetaKey(mainChainId), ((toString(newHeight) + SEP) + toBase16String(blockHash))), IntegerEntry(chainIdKey(i.originCaller.bytes), mainChainId), setEpochData(i.originCaller), IntegerEntry(lastEpochBlocksNumberKey, 1), StringEntry(epochMetaKey(height), ((toString(i.originCaller) + ",0,") + toBase16String(blockHash)))] ++ firstBlockId) ++ updateAllBlockIds)
445+ let newChainHeight = (mainChainHeight + 1)
446+ let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(mainChainLastBlock)) + i.originCaller.bytes)
447+[BinaryEntry((blockMetaK + blockHashHex), newBlockMeta), StringEntry(chainMetaKey(mainChainId), ((toString(newChainHeight) + SEP) + blockHashHex)), IntegerEntry(minerChainIdKey(i.originCaller), mainChainId), IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), newChainHeight), thisEpochMeta]
440448 }
441449 else throw("Strict value is not equal to itself.")
442450 }
450458
451459
452460 @Callable(i)
453-func startAltChain (blockHash,reference,epoch) = {
461+func startAltChain (blockHashHex,referenceHex,epoch) = {
454462 let checkEpoch = ensureCorrectEpoch(epoch)
455463 if ((checkEpoch == checkEpoch))
456464 then {
457- let checkReference = ensureReferenceIsSafeForRollback(reference)
458- if ((checkReference == checkReference))
465+ let checkGenerator = ensureMiningEpoch(i.originCaller)
466+ if ((checkGenerator == checkGenerator))
459467 then {
460- let checkGenerator = ensureMiningEpoch(i.originCaller)
461- if ((checkGenerator == checkGenerator))
468+ let $t01198112059 = blockMeta(referenceHex)
469+ let refChainHeight = $t01198112059._1
470+ let refEpoch = $t01198112059._2
471+ let refRef = $t01198112059._3
472+ let refGenerator = $t01198112059._4
473+ let epochRef = if ((refEpoch >= finalizedEpoch))
474+ then refEpoch
475+ else throw((((("can not start alt chain from epoch " + toString(refEpoch)) + ", epoch ") + toString(finalizedEpoch)) + " is finalized"))
476+ let checkChain = ensureExpectedOrInactiveChain(i.originCaller, mainChainId, referenceHex)
477+ if ((checkChain == checkChain))
462478 then {
463- let checkChain = ensureMainOrInactiveChain(i.originCaller)
464- if ((checkChain == checkChain))
465- then {
466- let newChainId = (valueOrElse(getInteger(lastChainIdKey), 0) + 1)
467- let newHeight = (blockMeta(reference)._1 + 1)
468- let newBlockMeta = (((toBytes(newHeight) + toBytes(height)) + reference) + i.originCaller.bytes)
469-[BinaryEntry(blockMetaKey(blockHash), newBlockMeta), BinaryEntry((blockMetaK + toBase16String(blockHash)), newBlockMeta), BinaryEntry(chainFirstBlockIdKey(newChainId), blockHash), BinaryEntry(chainLastBlockIdKey(newChainId), blockHash), StringEntry(chainAllBlockIdsKey(newChainId, 0), toBase16String(blockHash)), IntegerEntry(chainHeightKey(newChainId), newHeight), StringEntry(chainMetaKey(newChainId), ((toString(newHeight) + SEP) + toBase16String(blockHash))), IntegerEntry(chainIdKey(i.originCaller.bytes), newChainId), setEpochData(i.originCaller), StringEntry(supportersKey(newChainId), toString(i.originCaller)), IntegerEntry(lastEpochBlocksNumberKey, 1), IntegerEntry(lastChainIdKey, newChainId), StringEntry(epochMetaKey(height), ((toString(i.originCaller) + ",0,") + toBase16String(blockHash)))]
470- }
471- else throw("Strict value is not equal to itself.")
479+ let newChainId = (valueOrElse(getInteger(lastChainIdKey), 0) + 1)
480+ let referenceHeight = blockMeta(referenceHex)._1
481+ let newChainHeight = (referenceHeight + 1)
482+ let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(referenceHex)) + i.originCaller.bytes)
483+ let thisEpochMeta = match epochMeta(height) {
484+ case _: Unit =>
485+ StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(epochRef)) + SEP) + blockHashHex))
486+ case other =>
487+ throw("Epoch already started")
488+ }
489+[thisEpochMeta, BinaryEntry((blockMetaK + blockHashHex), newBlockMeta), StringEntry(chainFirstBlockIdKey(newChainId), blockHashHex), StringEntry(chainMetaKey(mainChainId), ((toString(newChainHeight) + SEP) + blockHashHex)), IntegerEntry(minerChainIdKey(i.originCaller), newChainId), IntegerEntry(chainLastHeightKey(newChainId, i.originCaller), newChainHeight), IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), referenceHeight), StringEntry(supportersKey(newChainId), toString(i.originCaller)), IntegerEntry(lastChainIdKey, newChainId)]
472490 }
473491 else throw("Strict value is not equal to itself.")
474492 }
480498
481499
482500 @Callable(i)
483-func extendAltChain (chainId,blockHash,reference,epoch) = {
501+func extendAltChain (chainId,blockHashHex,referenceHex,epoch) = {
484502 let checkEpoch = ensureCorrectEpoch(epoch)
485503 if ((checkEpoch == checkEpoch))
486504 then {
487505 let checkGenerator = ensureMiningEpoch(i.originCaller)
488506 if ((checkGenerator == checkGenerator))
489507 then {
490- let checkChain = ensureAltChainExtension(i.originCaller, chainId)
508+ let checkChain = ensureExpectedOrInactiveChain(i.originCaller, chainId, toBase16String(blockMeta(getStringValue(chainFirstBlockIdKey(chainId)))._3))
491509 if ((checkChain == checkChain))
492510 then {
493- let checkReference = isReferenceCorrect(reference, getBinary(chainLastBlockIdKey(chainId)))
511+ let $t01415914213 = chainMeta(chainId)
512+ let chainHeight = $t01415914213._1
513+ let chainLastBlock = $t01415914213._2
514+ let checkReference = isReferenceCorrect(referenceHex, chainLastBlock)
494515 if ((checkReference == checkReference))
495516 then {
496- let chainHeight = valueOrElse(getInteger(chainHeightKey(chainId)), 0)
497- let blockIdsLastN = valueOrElse(getInteger(chainAllBlockIdsLastNKey(chainId)), 0)
498- let blockIds = getStringValue(chainAllBlockIdsKey(chainId, blockIdsLastN))
499- let newHeight = (chainHeight + 1)
500- let updateAllBlockIds = if ((size(blockIds) >= MAX_BLOCKS_STR_SIZE))
501- then [IntegerEntry(chainAllBlockIdsLastNKey(chainId), (blockIdsLastN + 1)), StringEntry(chainAllBlockIdsKey(chainId, (blockIdsLastN + 1)), toBase16String(blockHash))]
502- else {
503- let newBlockIds = ((toBase16String(blockHash) + SEP) + blockIds)
504-[StringEntry(chainAllBlockIdsKey(chainId, blockIdsLastN), newBlockIds)]
505- }
517+ let newChainHeight = (chainHeight + 1)
506518 let mainChainEntry = if ((supportingBalance(chainId) > (computedTotalBalance / 2)))
507- then [IntegerEntry(mainChainIdKey, chainId)]
519+ then [IntegerEntry(mainChainIdKey, chainId), IntegerEntry(firstValidAltChainIdKey, (valueOrElse(getInteger(lastChainIdKey), 0) + 1))]
508520 else nil
509- let newBlockMeta = (((toBytes(newHeight) + toBytes(height)) + reference) + i.originCaller.bytes)
510- ((([BinaryEntry(blockMetaKey(blockHash), newBlockMeta), BinaryEntry((blockMetaK + toBase16String(blockHash)), newBlockMeta), BinaryEntry(chainLastBlockIdKey(chainId), blockHash), IntegerEntry(chainHeightKey(chainId), newHeight), StringEntry(chainMetaKey(chainId), ((toString(newHeight) + SEP) + toBase16String(blockHash))), setEpochData(i.originCaller), IntegerEntry(chainIdKey(i.originCaller.bytes), chainId), IntegerEntry(lastEpochBlocksNumberKey, 1), StringEntry(epochMetaKey(height), ((toString(i.originCaller) + ",0,") + toBase16String(blockHash)))] ++ mainChainEntry) ++ addSupporter(chainId, i.originCaller)) ++ updateAllBlockIds)
521+ let thisEpochMeta = match epochMeta(height) {
522+ case _: Unit =>
523+ StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(blockMeta(referenceHex)._2)) + SEP) + blockHashHex))
524+ case other =>
525+ throw("Epoch already started")
526+ }
527+ let updateMainChainLastMinedBlock = if (if ((mainChainEntry == nil))
528+ then (valueOrElse(minerChainId(i.originCaller), 0) != chainId)
529+ else false)
530+ then [IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), blockMeta(getStringValue(chainFirstBlockIdKey(chainId)))._1)]
531+ else nil
532+ let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(referenceHex)) + i.originCaller.bytes)
533+ ((([BinaryEntry((blockMetaK + blockHashHex), newBlockMeta), StringEntry(chainMetaKey(mainChainId), ((toString(newChainHeight) + SEP) + blockHashHex)), thisEpochMeta, IntegerEntry(minerChainIdKey(i.originCaller), chainId), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight)] ++ mainChainEntry) ++ addSupporter(chainId, i.originCaller)) ++ updateMainChainLastMinedBlock)
511534 }
512535 else throw("Strict value is not equal to itself.")
513536 }
531554 unit
532555 }
533556
534- if ((MIN_BALANCE > wavesBalance(i.originCaller).generating))
535- then throw(((("Insufficient generating balance: " + toString(wavesBalance(i.originCaller).generating)) + ". Required: ") + toString(MIN_BALANCE)))
536- else if ((size(rewardAddress) != 20))
537- then throw("rewardAddress should be an L2 address")
538- else {
539- func checkExistence (exists,miner) = if (exists)
540- then true
541- else (miner == toString(i.originCaller))
557+ if (!(isContractSetup()))
558+ then throw("The contract has not yet set up")
559+ else if ((MIN_BALANCE > wavesBalance(i.originCaller).generating))
560+ then throw(((("Insufficient generating balance: " + toString(wavesBalance(i.originCaller).generating)) + ". Required: ") + toString(MIN_BALANCE)))
561+ else if ((size(rewardAddress) != 20))
562+ then throw("rewardAddress should be an L2 address")
563+ else {
564+ func checkExistence (exists,miner) = if (exists)
565+ then true
566+ else (miner == toString(i.originCaller))
542567
543- let alreadyExists = {
544- let $l = allMiners
545- let $s = size($l)
546- let $acc0 = false
547- func $f0_1 ($a,$i) = if (($i >= $s))
548- then $a
549- else checkExistence($a, $l[$i])
568+ let alreadyExists = {
569+ let $l = allMiners
570+ let $s = size($l)
571+ let $acc0 = false
572+ func $f0_1 ($a,$i) = if (($i >= $s))
573+ then $a
574+ else checkExistence($a, $l[$i])
550575
551- func $f0_2 ($a,$i) = if (($i >= $s))
552- then $a
553- else throw("List size exceeds 50")
576+ func $f0_2 ($a,$i) = if (($i >= $s))
577+ then $a
578+ else throw("List size exceeds 50")
554579
555- $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)
580+ $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)
581+ }
582+ if (alreadyExists)
583+ then nil
584+ else {
585+ let newMiner = toString(i.originCaller)
586+ let rewardAddressHex = toBase16String(rewardAddress)
587+ let check = checkRewardAddress(rewardAddressHex)
588+ if ((check == check))
589+ then {
590+ let newMiners = if ((size(allMiners) == 0))
591+ then newMiner
592+ else ((allMinersStr + SEP) + newMiner)
593+ let deleteOldRewardAddressPk = match getBinary(minerRewardAddressKey(newMiner)) {
594+ case oldAddress: ByteVector =>
595+ if ((oldAddress == rewardAddress))
596+ then nil
597+ else [DeleteEntry(minerPkKey(toBase16String(oldAddress)))]
598+ case _ =>
599+ nil
600+ }
601+ ([StringEntry(allMinersKey, newMiners), StringEntry(minerRewardAddressKey(newMiner), ("0x" + rewardAddressHex)), BinaryEntry(minerPkKey(rewardAddressHex), i.originCallerPublicKey)] ++ deleteOldRewardAddressPk)
602+ }
603+ else throw("Strict value is not equal to itself.")
604+ }
556605 }
557- if (alreadyExists)
558- then nil
559- else {
560- let newMiner = toString(i.originCaller)
561- let rewardAddressHex = toBase16String(rewardAddress)
562- let check = checkRewardAddress(rewardAddressHex)
563- if ((check == check))
564- then {
565- func filterLeaving (acc,miner) = {
566- let $t01829418326 = acc
567- let accMiners = $t01829418326._1
568- let isUpdated = $t01829418326._2
569- if ((miner == newMiner))
570- then $Tuple2(accMiners, true)
571- else $Tuple2((accMiners :+ miner), isUpdated)
572- }
573-
574- let newMiners = if ((size(allMiners) == 0))
575- then newMiner
576- else ((makeString_2C(allMiners, SEP) + SEP) + newMiner)
577- let $t01859018680 = {
578- let $l = allLeavingMiners
579- let $s = size($l)
580- let $acc0 = $Tuple2(nil, false)
581- func $f1_1 ($a,$i) = if (($i >= $s))
582- then $a
583- else filterLeaving($a, $l[$i])
584-
585- func $f1_2 ($a,$i) = if (($i >= $s))
586- then $a
587- else throw("List size exceeds 50")
588-
589- $f1_2($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_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)
590- }
591- let newLeavingMiners = $t01859018680._1
592- let isUpdated = $t01859018680._2
593- let newLeavingMinersAction = if ((newLeavingMiners != nil))
594- then [StringEntry(leavingMinersKey(height), makeString_2C(newLeavingMiners, SEP))]
595- else if ((allLeavingMiners != nil))
596- then [DeleteEntry(leavingMinersKey(height))]
597- else nil
598- let joinHeight = if (isUpdated)
599- then height
600- else (height + 1)
601- let deleteOldRewardAddressPk = match getBinary(minerRewardAddress(newMiner)) {
602- case oldAddress: ByteVector =>
603- if ((oldAddress == rewardAddress))
604- then nil
605- else [DeleteEntry(minerPkKey(toBase16String(oldAddress)))]
606- case _ =>
607- nil
608- }
609- (([StringEntry(allMinersKey, newMiners), IntegerEntry(minerJoinHeightKey(newMiner), joinHeight), BinaryEntry(minerRewardAddress(newMiner), rewardAddress), BinaryEntry(minerPkKey(rewardAddressHex), i.originCallerPublicKey)] ++ newLeavingMinersAction) ++ deleteOldRewardAddressPk)
610- }
611- else throw("Strict value is not equal to itself.")
612- }
613- }
614606 }
615607
616608
618610 @Callable(i)
619611 func leave () = {
620612 let leavingMiner = toString(i.originCaller)
621- func filterMiner (acc,miner) = if ((miner == leavingMiner))
613+ func skipLeavingMiner (acc,miner) = if ((miner == leavingMiner))
622614 then acc
623615 else (acc :+ miner)
624616
625- let restMiners = {
617+ let remainingMiners = {
626618 let $l = allMiners
627619 let $s = size($l)
628620 let $acc0 = nil
629621 func $f0_1 ($a,$i) = if (($i >= $s))
630622 then $a
631- else filterMiner($a, $l[$i])
623+ else skipLeavingMiner($a, $l[$i])
632624
633625 func $f0_2 ($a,$i) = if (($i >= $s))
634626 then $a
636628
637629 $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)
638630 }
639- if ((thisEpochMiner == i.originCaller.bytes))
631+ let rewardAddrKey = minerRewardAddressKey(leavingMiner)
632+ let prevRewardAddress = valueOrErrorMessage(getBinary(this, rewardAddrKey), "miner has never joined")
633+ if ((thisEpochMiner == i.originCaller))
640634 then throw("designated miner can't leave")
641- else match getInteger(minerJoinHeightKey(leavingMiner)) {
642- case minerHeight: Int =>
643- if ((minerHeight > height))
644- then [StringEntry(allMinersKey, makeString_2C(restMiners, SEP)), DeleteEntry(minerJoinHeightKey(leavingMiner))]
645- else {
646- let oldLeavingMiners = valueOrElse(getString(leavingMinersKey(height)), "")
647- let newLeavingMiners = if ((oldLeavingMiners == ""))
648- then leavingMiner
649- else ((oldLeavingMiners + SEP) + leavingMiner)
650-[StringEntry(allMinersKey, makeString_2C(restMiners, SEP)), DeleteEntry(minerJoinHeightKey(leavingMiner)), StringEntry(leavingMinersKey(height), newLeavingMiners)]
651- }
652- case _ =>
653-[StringEntry(allMinersKey, makeString_2C(restMiners, SEP))]
635+ else [StringEntry(allMinersKey, makeString_2C(remainingMiners, SEP)), DeleteEntry(rewardAddrKey), DeleteEntry(minerPkKey(toBase16String(prevRewardAddress)))]
636+ }
637+
638+
639+
640+@Callable(i)
641+func setup (genesisBlockHashHex,minerReward,stakingContractAddressB58) = if (isContractSetup())
642+ then throw("The contract has been already set up")
643+ else {
644+ let genesisBlockHash = fromBase16String(genesisBlockHashHex)
645+ let emptyPk = base58'11111111111111111111111111111111'
646+ let genesisMinerAddress = addressFromPublicKey(emptyPk)
647+ let genesisEthRewardAddress = base58'11111111111111111111'
648+ let genesisBlockReferenceHash = base58'11111111111111111111111111111111'
649+ let genesisBlockMeta = (((toBytes(0) + toBytes(height)) + genesisBlockReferenceHash) + genesisMinerAddress.bytes)
650+[BinaryEntry((blockMetaK + genesisBlockHashHex), genesisBlockMeta), BinaryEntry(chainFirstBlockIdKey(0), genesisBlockHash), StringEntry(chainMetaKey(0), ("0," + genesisBlockHashHex)), IntegerEntry(minerRewardKey, minerReward), StringEntry(stakingContractAddressKey, stakingContractAddressB58), StringEntry(epochMetaKey(height), ((toString(genesisMinerAddress) + ",0,") + genesisBlockHashHex))]
654651 }
655- }
656652
657653
Full:
OldNewDifferences
11 {-# STDLIB_VERSION 8 #-}
22 {-# SCRIPT_TYPE ACCOUNT #-}
33 {-# CONTENT_TYPE DAPP #-}
44 let INT_MAX = 9223372036854775807
55
66 let WAVES = 100000000
77
88 let MIN_BALANCE = (20000 * WAVES)
99
10-let MAX_BLOCKS_AT_EPOCH = 50
11-
1210 let SEP = ","
1311
1412 let BLOCK_HASH_SIZE = 32
1513
1614 let ADDRESS_SIZE = 26
17-
18-let MAX_BLOCKS_STR_SIZE = ((64 * MAX_BLOCKS_AT_EPOCH) + (MAX_BLOCKS_AT_EPOCH - 1))
1915
2016 let thisEpochDataKey = "thisEpochData"
2117
2218 let allMinersKey = "allMiners"
2319
2420 let mainChainIdKey = "mainChainId"
2521
2622 let lastChainIdKey = "lastChainId"
2723
28-let lastEpochBlocksNumberKey = "lastEpochBlocksNumber"
24+let firstValidAltChainIdKey = "firstValidAltChainId"
2925
3026 let minerRewardKey = "minerReward"
27+
28+let stakingContractAddressKey = "stakingContractAddress"
3129
3230 let blockMetaK = "block_0x"
3331
3432 func pad (i) = {
3533 let s = toString(i)
3634 match size(s) {
3735 case _ =>
3836 if ((1 == $match0))
3937 then ("0000000" + s)
4038 else if ((2 == $match0))
4139 then ("000000" + s)
4240 else if ((3 == $match0))
4341 then ("00000" + s)
4442 else if ((4 == $match0))
4543 then ("0000" + s)
4644 else if ((5 == $match0))
4745 then ("000" + s)
4846 else if ((6 == $match0))
4947 then ("00" + s)
5048 else if ((7 == $match0))
5149 then ("0" + s)
5250 else s
5351 }
5452 }
5553
5654
5755 func epochMetaKey (epoch) = ("epoch_" + pad(epoch))
5856
5957
58+func chainFirstBlockIdKey (chainId) = (("chain" + toString(chainId)) + "FirstBlock")
59+
60+
6061 func chainMetaKey (chainId) = ("chain_" + pad(chainId))
6162
6263
63-let stakingContractAddress = Address(getBinaryValue(this, "stakingContractAddress"))
64+func chainLastHeightKey (chainId,miner) = ((("chain_" + pad(chainId)) + "_") + toString(miner))
65+
66+
67+func supportersKey (chainId) = (("chain" + toString(chainId)) + "Supporters")
68+
69+
70+func minerRewardAddressKey (minerAddr) = (("miner_" + minerAddr) + "_RewardAddress")
71+
72+
73+func minerPkKey (rewardAddress) = (("miner_0x" + rewardAddress) + "_PK")
74+
75+
76+func minerChainIdKey (miner) = (("miner_" + toString(miner)) + "_ChainId")
77+
78+
79+let stakingContractAddress = match getString(this, stakingContractAddressKey) {
80+ case s: String =>
81+ valueOrErrorMessage(addressFromString(s), ("invalid staking contract address: " + s))
82+ case _ =>
83+ Address(getBinaryValue(this, stakingContractAddressKey))
84+}
6485
6586 func generatingBalance (address) = match getString(stakingContractAddress, ("%s__" + toString(address))) {
6687 case str: String =>
6788 let paramList = split(str, "__")
6889 let prevHeight = parseIntValue(paramList[1])
6990 let prevBalance = parseIntValue(paramList[2])
7091 let nextHeight = parseIntValue(paramList[3])
7192 let nextBalance = parseIntValue(paramList[4])
7293 if ((height >= nextHeight))
7394 then nextBalance
7495 else if ((height >= prevHeight))
7596 then prevBalance
7697 else 0
7798 case _ =>
7899 0
79100 }
80101
81102
82-func blockMetaKey (blockId) = ("blockMeta" + toBase16String(blockId))
83-
84-
85-func chainIdKey (generator) = ("chainIdOf" + toBase16String(generator))
86-
87-
88-func chainHeightKey (chainId) = (("chain" + toString(chainId)) + "Height")
89-
90-
91-func chainFirstBlockIdKey (chainId) = (("chain" + toString(chainId)) + "FirstBlock")
92-
93-
94-func chainLastBlockIdKey (chainId) = (("chain" + toString(chainId)) + "LastBlock")
95-
96-
97-func chainAllBlockIdsKey (chainId,n) = ((("chain" + toString(chainId)) + "AllBlocks") + toString(n))
98-
99-
100-func chainAllBlockIdsLastNKey (chainId) = (("chain" + toString(chainId)) + "AllBlocksLastN")
101-
102-
103-func supportersKey (chainId) = (("chain" + toString(chainId)) + "Supporters")
104-
105-
106-func minerRewardAddress (minerAddr) = (("miner" + minerAddr) + "RewardAddress")
107-
108-
109-func minerJoinHeightKey (minerAddr) = (("miner" + minerAddr) + "JoinHeight")
110-
111-
112-func minerPkKey (rewardAddress) = (("miner" + rewardAddress) + "PK")
113-
114-
115-func leavingMinersKey (epoch) = ("leavingMinersAt" + toString(epoch))
103+func chainMeta (chainId) = {
104+ let s = getStringValue(chainMetaKey(chainId))
105+ let items = split(s, SEP)
106+ $Tuple2(parseIntValue(items[0]), items[1])
107+ }
116108
117109
118110 let mainChainId = valueOrElse(getInteger(mainChainIdKey), 0)
119111
120-let thisEpochMiner = match getString(thisEpochDataKey) {
121- case rawThisEpochData: String =>
122- let thisEpochData = split(rawThisEpochData, SEP)
123- let thisEpoch = parseIntValue(thisEpochData[0])
124- if ((thisEpoch == height))
125- then fromBase58String(thisEpochData[1])
126- else unit
112+let $t027802846 = chainMeta(mainChainId)
113+
114+let mainChainHeight = $t027802846._1
115+
116+let mainChainLastBlock = $t027802846._2
117+
118+func epochMeta (epoch) = match getString(epochMetaKey(epoch)) {
119+ case s: String =>
120+ let fragments = split(s, SEP)
121+ $Tuple3(addressFromStringValue(fragments[0]), parseIntValue(fragments[1]), fragments[2])
127122 case _ =>
128123 unit
129124 }
130125
131-let allMiners = match getString(allMinersKey) {
132- case raw: String =>
133- split_4C(raw, SEP)
126+
127+let $t030783542 = match epochMeta(height) {
128+ case m: (Address, Int, String) =>
129+ m
134130 case _ =>
135- nil
131+ match getString(thisEpochDataKey) {
132+ case rawThisEpochData: String =>
133+ let thisEpochData = split(rawThisEpochData, SEP)
134+ let thisEpoch = parseIntValue(thisEpochData[0])
135+ $Tuple3(if ((thisEpoch == height))
136+ then addressFromStringValue(thisEpochData[1])
137+ else unit, 0, "")
138+ case _ =>
139+ $Tuple3(unit, 0, "")
140+ }
136141 }
137142
138-let allLeavingMiners = match getString(leavingMinersKey(height)) {
139- case raw: String =>
140- split_4C(raw, SEP)
143+let thisEpochMiner = $t030783542._1
144+
145+let thisEpochRef = $t030783542._2
146+
147+let thisEpochLastBlock = $t030783542._3
148+
149+let allMinersStr = valueOrElse(getString(allMinersKey), "")
150+
151+let allMiners = match allMinersStr {
141152 case _ =>
142- nil
153+ if (("" == $match0))
154+ then nil
155+ else if ($isInstanceOf($match0, "String"))
156+ then {
157+ let raw = $match0
158+ split_4C(raw, SEP)
159+ }
160+ else throw("Match error")
143161 }
144162
145163 func blockMeta (blockId) = {
146- let meta = getBinaryValue(blockMetaKey(blockId))
147- let metaWithoutHeight = drop(meta, 8)
148- let metaWithoutHeightEpoch = drop(metaWithoutHeight, 8)
149- let blockHeight = toInt(take(meta, 8))
150- let blockEpoch = toInt(take(metaWithoutHeight, 8))
151- let blockParent = take(metaWithoutHeightEpoch, BLOCK_HASH_SIZE)
164+ let meta = getBinaryValue((blockMetaK + blockId))
165+ let blockHeight = toInt(meta)
166+ let blockEpoch = toInt(meta, 8)
167+ let blockParent = take(drop(meta, 16), BLOCK_HASH_SIZE)
152168 let blockGenerator = takeRight(meta, ADDRESS_SIZE)
153169 $Tuple4(blockHeight, blockEpoch, blockParent, blockGenerator)
154170 }
155171
156172
157-func setEpochData (generator) = StringEntry(thisEpochDataKey, ((toString(height) + SEP) + toBase58String(generator.bytes)))
173+func lastHeightBy (miner,chainId) = match getInteger(chainLastHeightKey(chainId, miner)) {
174+ case h: Int =>
175+ h
176+ case _ =>
177+ let blockHash = getStringValue(((("chain" + toString(chainId)) + "LastMinedBy") + toString(miner)))
178+ blockMeta(blockHash)._1
179+}
158180
159181
160-let $t041315188 = {
182+let $t043025268 = {
161183 let hitSource = match lastBlock.vrf {
162184 case vrf: ByteVector =>
163185 vrf
164186 case _ =>
165187 lastBlock.generationSignature
166188 }
167189 func processMiner (prev,miner) = {
168- let $t044294492 = prev
169- let prevDelay = $t044294492._1
170- let prevMiner = $t044294492._2
171- let prevTotalBalance = $t044294492._3
172- let prevMiners = $t044294492._4
190+ let $t046004663 = prev
191+ let prevDelay = $t046004663._1
192+ let prevMiner = $t046004663._2
193+ let prevTotalBalance = $t046004663._3
194+ let prevMiners = $t046004663._4
173195 let minerAddress = addressFromStringValue(miner)
174196 let wavesGenBalance = wavesBalance(minerAddress).generating
175197 let minerBalance = generatingBalance(minerAddress)
176- if (if (if ((MIN_BALANCE > wavesGenBalance))
198+ if (if ((MIN_BALANCE > wavesGenBalance))
177199 then true
178200 else (0 >= minerBalance))
179- then true
180- else (valueOrElse(getInteger(minerJoinHeightKey(miner)), INT_MAX) > height))
181201 then prev
182202 else {
183203 let nextDelay = calculateDelay(minerAddress, minerBalance)
184204 if ((prevDelay > nextDelay))
185205 then $Tuple4(nextDelay, miner, (prevTotalBalance + minerBalance), (prevMiners :+ miner))
186206 else $Tuple4(prevDelay, prevMiner, (prevTotalBalance + minerBalance), (prevMiners :+ miner))
187207 }
188208 }
189209
190- let $l = (allMiners ++ allLeavingMiners)
210+ let $l = allMiners
191211 let $s = size($l)
192212 let $acc0 = $Tuple4(INT_MAX, "", 0, nil)
193213 func $f0_1 ($a,$i) = if (($i >= $s))
194214 then $a
195215 else processMiner($a, $l[$i])
196216
197217 func $f0_2 ($a,$i) = if (($i >= $s))
198218 then $a
199219 else throw("List size exceeds 50")
200220
201221 $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)
202222 }
203223
204-let computedDelay = $t041315188._1
224+let computedDelay = $t043025268._1
205225
206-let computedGenerator = $t041315188._2
226+let computedGenerator = $t043025268._2
207227
208-let computedTotalBalance = $t041315188._3
228+let computedTotalBalance = $t043025268._3
209229
210-let filteredMiners = $t041315188._4
230+let filteredMiners = $t043025268._4
211231
212-let safeRollbackHeight = {
213- func addBalance (acc,blockIdStr) = {
214- let $t053025346 = acc
215- let totalBalance = $t053025346._1
216- let prevId = $t053025346._2
217- let generators = $t053025346._3
218- if ((totalBalance > (computedTotalBalance / 2)))
219- then acc
220- else {
221- let blockId = fromBase16String(blockIdStr)
222- let generator = Address(blockMeta(blockId)._4)
223- if (containsElement(generators, generator))
224- then acc
225- else {
226- let balance = generatingBalance(generator)
227- $Tuple3((totalBalance + balance), blockId, (generators :+ generator))
228- }
229- }
232+func getChainLastBlockId (chainId) = chainMeta(chainId)._2
233+
234+
235+let $t053345436 = blockMeta(mainChainLastBlock)
236+
237+let ignored = $t053345436._1
238+
239+let mainChainEpoch = $t053345436._2
240+
241+let mainChainParentHash = $t053345436._3
242+
243+let mainChainGenerator = $t053345436._4
244+
245+let $t054386554 = {
246+ let offsets_200 = split("::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::", "")
247+ let halfBalance = (computedTotalBalance / 2)
248+ func step (prev,next) = {
249+ let $t058515915 = prev
250+ let thisEpoch = $t058515915._1
251+ let totalBalance = $t058515915._2
252+ let maybeSafeEpoch = $t058515915._3
253+ let prevMiners = $t058515915._4
254+ match maybeSafeEpoch {
255+ case _: Unit =>
256+ let $t059736041 = value(epochMeta(thisEpoch))
257+ let miner = $t059736041._1
258+ let prevEpoch = $t059736041._2
259+ let lastBlockHash = $t059736041._3
260+ let $t060506246 = if (containsElement(prevMiners, miner))
261+ then $Tuple2(totalBalance, prevMiners)
262+ else $Tuple2((totalBalance + generatingBalance(miner)), miner :: prevMiners)
263+ let newTotalBalance = $t060506246._1
264+ let newMiners = $t060506246._2
265+ if ((newTotalBalance >= halfBalance))
266+ then $Tuple4(thisEpoch, newTotalBalance, thisEpoch, allMiners)
267+ else $Tuple4(prevEpoch, newTotalBalance, unit, newMiners)
268+ case _ =>
269+ prev
270+ }
230271 }
231272
232- func getBlockIds (n) = {
233- let rawStr = valueOrElse(getString(chainAllBlockIdsKey(mainChainId, n)), "")
234- if ((rawStr == ""))
235- then nil
236- else split_4C(rawStr, SEP)
237- }
238-
239- let lastN = valueOrElse(getInteger(chainAllBlockIdsLastNKey(mainChainId)), 0)
240- let allBlocksIds = (((getBlockIds(lastN) ++ getBlockIds((lastN - 1))) ++ getBlockIds((lastN - 2))) ++ getBlockIds((lastN - 3)))
241- let $t061146188 = {
242- let $l = allBlocksIds
273+ let res = {
274+ let $l = offsets_200
243275 let $s = size($l)
244- let $acc0 = $Tuple3(0, base58'', nil)
276+ let $acc0 = $Tuple4(mainChainEpoch, 0, unit, nil)
245277 func $f0_1 ($a,$i) = if (($i >= $s))
246278 then $a
247- else addBalance($a, $l[$i])
279+ else step($a, $l[$i])
248280
249281 func $f0_2 ($a,$i) = if (($i >= $s))
250282 then $a
251283 else throw("List size exceeds 200")
252284
253285 $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)
254286 }
255- let _b = $t061146188._1
256- let blockId = $t061146188._2
257- blockMeta(blockId)._1
287+ valueOrElse(res._3, $Tuple2(1, ""))
258288 }
289+
290+let finalizedEpoch = $t054386554._1
291+
292+let finalizedBlockHash = $t054386554._2
259293
260294 func supportingBalance (chainId) = {
261295 func addBalance (acc,generatorStr) = {
262- let $t063336369 = acc
263- let totalBalance = $t063336369._1
264- let generators = $t063336369._2
296+ let $t066726708 = acc
297+ let totalBalance = $t066726708._1
298+ let generators = $t066726708._2
265299 let generator = addressFromStringValue(generatorStr)
266300 if (containsElement(generators, generator))
267301 then acc
268302 else {
269303 let balance = generatingBalance(generator)
270304 $Tuple2((totalBalance + balance), (generators :+ generator))
271305 }
272306 }
273307
274308 let allGenerators = split_4C(getStringValue(supportersKey(chainId)), SEP)
275- let $t066916756 = {
309+ let $t070307095 = {
276310 let $l = allGenerators
277311 let $s = size($l)
278312 let $acc0 = $Tuple2(0, nil)
279313 func $f0_1 ($a,$i) = if (($i >= $s))
280314 then $a
281315 else addBalance($a, $l[$i])
282316
283317 func $f0_2 ($a,$i) = if (($i >= $s))
284318 then $a
285319 else throw("List size exceeds 100")
286320
287321 $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)
288322 }
289- let balance = $t066916756._1
290- let _g = $t066916756._2
323+ let balance = $t070307095._1
324+ let _g = $t070307095._2
291325 balance
292326 }
327+
328+
329+func isContractSetup () = isDefined(getInteger(minerRewardKey))
293330
294331
295332 func ensureMiningEpoch (generator) = if ((toString(generator) != computedGenerator))
296333 then throw(((((toBase58String(generator.bytes) + " is not allowed to mine in ") + toString(height)) + " epoch. Expected ") + computedGenerator))
297334 else unit
298335
299336
300-func isReferenceCorrect (reference,lastBlock) = match lastBlock {
301- case _: Unit =>
302- unit
303- case lb: ByteVector =>
304- if ((reference == lb))
305- then unit
306- else throw(((("Expected a reference to the chain last block: " + toBase16String(lb)) + ". Got: ") + toBase16String(reference)))
307- case _ =>
308- throw("Match error")
309-}
337+func isReferenceCorrect (reference,lastBlock) = if ((reference == lastBlock))
338+ then unit
339+ else throw(((("Expected a reference to the chain last block: 0x" + lastBlock) + ". Got: 0x") + reference))
310340
311341
312342 func chainIsInactive (chainId) = {
313343 let firstBlockId = getBinaryValue(chainFirstBlockIdKey(chainId))
314- (safeRollbackHeight > blockMeta(firstBlockId)._1)
344+ let firstValidAltChainId = valueOrElse(getInteger(firstValidAltChainIdKey), 0)
345+ if ((firstValidAltChainId > chainId))
346+ then true
347+ else (finalizedEpoch > blockMeta(toBase16String(firstBlockId))._1)
315348 }
316349
317350
318-func ensureMainOrInactiveChain (generator) = match getInteger(chainIdKey(generator.bytes)) {
319- case currentId: Int =>
320- if (if ((currentId == mainChainId))
321- then true
322- else chainIsInactive(currentId))
323- then unit
324- else throw(("miner is mining other chain " + toString(currentId)))
325- case _ =>
326- unit
327-}
351+func minerChainId (miner) = valueOrElse(getInteger(minerChainIdKey(miner)), getInteger(("chainIdOf" + toString(miner))))
328352
329353
330-func ensureAltChainExtension (generator,chainId) = match getInteger(chainIdKey(generator.bytes)) {
331- case currentId: Int =>
332- if ((currentId == chainId))
333- then unit
334- else {
335- let extendingFirstBlockId = getBinaryValue(chainFirstBlockIdKey(chainId))
336- let minerCurrentFirstBlockId = getBinaryValue(chainFirstBlockIdKey(currentId))
337- let extendingFirstBlockHeight = blockMeta(extendingFirstBlockId)._1
338- let minerCurrentFirstBlockHeight = blockMeta(minerCurrentFirstBlockId)._1
339- if ((extendingFirstBlockHeight > minerCurrentFirstBlockHeight))
340- then unit
341- else throw(((((("miner is mining other chain " + toString(currentId)) + ", height of the alt chain first block ") + toString(extendingFirstBlockHeight)) + " is not less than height of the miner active chain first block ") + toString(minerCurrentFirstBlockHeight)))
342- }
343- case _ =>
344- unit
345-}
346-
347-
348-func ensureReferenceIsSafeForRollback (reference) = {
349- let startHeight = blockMeta(reference)._1
350- if ((safeRollbackHeight > startHeight))
351- then throw(((("start height " + toString(startHeight)) + " is less than minimum height ") + toString(safeRollbackHeight)))
352- else unit
354+func ensureExpectedOrInactiveChain (generator,expectedChainId,checkHeightBlock) = {
355+ let heightIsCorrect = match checkHeightBlock {
356+ case blockHash: String =>
357+ let lastMinedBlockHeight = lastHeightBy(generator, mainChainId)
358+ ((blockMeta(blockHash)._1 + 1) > lastMinedBlockHeight)
359+ case _ =>
360+ true
361+ }
362+ match minerChainId(generator) {
363+ case currentId: Int =>
364+ if (if ((currentId == expectedChainId))
365+ then true
366+ else if (chainIsInactive(currentId))
367+ then heightIsCorrect
368+ else false)
369+ then unit
370+ else throw(("miner is mining other chain " + toString(currentId)))
371+ case _ =>
372+ unit
373+ }
353374 }
354375
355376
356377 func ensureCorrectEpoch (epoch) = if ((epoch == height))
357378 then unit
358379 else throw(((("Expected block from epoch " + toString(height)) + ". Got ") + toString(epoch)))
359-
360-
361-func checkBlocksNumber () = if ((MAX_BLOCKS_AT_EPOCH > getIntegerValue(lastEpochBlocksNumberKey)))
362- then unit
363- else throw("Max blocks number at epoch reached")
364380
365381
366382 func addSupporter (chainId,generator) = {
367383 let supportersStr = getStringValue(supportersKey(chainId))
368384 let supporters = split(supportersStr, SEP)
369385 if (containsElement(supporters, toString(generator)))
370386 then nil
371387 else [StringEntry(supportersKey(chainId), ((supportersStr + SEP) + toString(generator)))]
372388 }
373389
374390
391+func validateBlockHash (hexStr) = {
392+ let decodedBytes = fromBase16String(hexStr)
393+ if ((size(decodedBytes) != 32))
394+ then throw("invalid block id length")
395+ else hexStr
396+ }
397+
398+
375399 @Callable(i)
376-func appendBlock (blockHash,reference) = {
377- let check = checkBlocksNumber()
378- if ((check == check))
379- then if ((thisEpochMiner != i.originCaller.bytes))
380- then match thisEpochMiner {
381- case epochMiner: ByteVector =>
382- throw(("not allowed to forge blocks in this epoch, expected from " + toBase58String(epochMiner)))
383- case _ =>
384- throw("not allowed to forge blocks in this epoch, epoch miner is absent")
385- }
386- else {
387- let chainId = valueOrElse(getInteger(chainIdKey(i.originCaller.bytes)), mainChainId)
388- let lastBlockId = getBinaryValue(chainLastBlockIdKey(chainId))
389- let check2 = isReferenceCorrect(reference, lastBlockId)
390- if ((check2 == check2))
391- then {
392- let chainHeight = valueOrElse(getInteger(chainHeightKey(chainId)), 0)
393- let newHeight = (chainHeight + 1)
394- let blocksNumber = valueOrElse(getInteger(lastEpochBlocksNumberKey), 0)
395- let newBlockMeta = (((toBytes(newHeight) + toBytes(height)) + lastBlockId) + i.originCaller.bytes)
396-[BinaryEntry(blockMetaKey(blockHash), newBlockMeta), BinaryEntry((blockMetaK + toBase16String(blockHash)), newBlockMeta), IntegerEntry(chainHeightKey(chainId), newHeight), BinaryEntry(chainLastBlockIdKey(chainId), blockHash), StringEntry(chainMetaKey(chainId), ((toString(newHeight) + SEP) + toBase16String(blockHash))), IntegerEntry(lastEpochBlocksNumberKey, (blocksNumber + 1)), StringEntry(epochMetaKey(height), ((toString(i.originCaller) + ",0,") + toBase16String(blockHash)))]
397- }
398- else throw("Strict value is not equal to itself.")
400+func appendBlock (blockHashHex,referenceHex) = if ((thisEpochMiner != i.originCaller))
401+ then match thisEpochMiner {
402+ case epochMiner: Address =>
403+ throw(("not allowed to forge blocks in this epoch, expected from " + toString(epochMiner)))
404+ case _ =>
405+ throw("not allowed to forge blocks in this epoch, epoch miner is absent")
406+ }
407+ else {
408+ let chainId = valueOrElse(minerChainId(i.originCaller), mainChainId)
409+ let $t098509901 = chainMeta(chainId)
410+ let chainHeight = $t098509901._1
411+ let lastBlockId = $t098509901._2
412+ let checkReference = isReferenceCorrect(referenceHex, lastBlockId)
413+ if ((checkReference == checkReference))
414+ then {
415+ let newChainHeight = (chainHeight + 1)
416+ let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(lastBlockId)) + i.originCaller.bytes)
417+ let blockHash = validateBlockHash(blockHashHex)
418+[BinaryEntry((blockMetaK + blockHashHex), newBlockMeta), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight), StringEntry(chainMetaKey(chainId), ((toString(newChainHeight) + SEP) + blockHashHex)), StringEntry(epochMetaKey(height), ((((toString(value(thisEpochMiner)) + SEP) + toString(thisEpochRef)) + SEP) + blockHashHex))]
399419 }
400- else throw("Strict value is not equal to itself.")
401- }
420+ else throw("Strict value is not equal to itself.")
421+ }
402422
403423
404424
405425 @Callable(i)
406-func extendMainChain (blockHash,reference,epoch) = {
426+func extendMainChain (blockHashHex,referenceHex,epoch) = {
407427 let checkEpoch = ensureCorrectEpoch(epoch)
408428 if ((checkEpoch == checkEpoch))
409429 then {
410430 let checkGenerator = ensureMiningEpoch(i.originCaller)
411431 if ((checkGenerator == checkGenerator))
412432 then {
413- let checkChain = ensureMainOrInactiveChain(i.originCaller)
433+ let checkChain = ensureExpectedOrInactiveChain(i.originCaller, mainChainId, unit)
414434 if ((checkChain == checkChain))
415435 then {
416- let chainHeight = valueOrElse(getInteger(chainHeightKey(mainChainId)), 0)
417- let lastValidBlockId = getBinary(chainLastBlockIdKey(mainChainId))
418- let checkReference = isReferenceCorrect(reference, lastValidBlockId)
436+ let checkReference = isReferenceCorrect(referenceHex, mainChainLastBlock)
419437 if ((checkReference == checkReference))
420438 then {
421- let blockIdsLastN = valueOrElse(getInteger(chainAllBlockIdsLastNKey(mainChainId)), 0)
422- let blockIds = valueOrElse(getString(chainAllBlockIdsKey(mainChainId, blockIdsLastN)), "")
423- let newHeight = (chainHeight + 1)
424- let updateAllBlockIds = if ((size(blockIds) >= MAX_BLOCKS_STR_SIZE))
425- then [IntegerEntry(chainAllBlockIdsLastNKey(mainChainId), (blockIdsLastN + 1)), StringEntry(chainAllBlockIdsKey(mainChainId, (blockIdsLastN + 1)), toBase16String(blockHash))]
426- else {
427- let newBlockIds = if ((blockIds == ""))
428- then toBase16String(blockHash)
429- else ((toBase16String(blockHash) + SEP) + blockIds)
430-[StringEntry(chainAllBlockIdsKey(mainChainId, blockIdsLastN), newBlockIds)]
431- }
432- let firstBlockId = match lastValidBlockId {
433- case id: ByteVector =>
434- nil
435- case _ =>
436-[BinaryEntry(chainFirstBlockIdKey(mainChainId), blockHash)]
439+ let thisEpochMeta = match epochMeta(height) {
440+ case _: Unit =>
441+ StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(mainChainEpoch)) + SEP) + blockHashHex))
442+ case other =>
443+ throw("Epoch already started")
437444 }
438- let newBlockMeta = (((toBytes(newHeight) + toBytes(height)) + reference) + i.originCaller.bytes)
439- (([BinaryEntry(blockMetaKey(blockHash), newBlockMeta), BinaryEntry((blockMetaK + toBase16String(blockHash)), newBlockMeta), IntegerEntry(chainHeightKey(mainChainId), newHeight), BinaryEntry(chainLastBlockIdKey(mainChainId), blockHash), StringEntry(chainMetaKey(mainChainId), ((toString(newHeight) + SEP) + toBase16String(blockHash))), IntegerEntry(chainIdKey(i.originCaller.bytes), mainChainId), setEpochData(i.originCaller), IntegerEntry(lastEpochBlocksNumberKey, 1), StringEntry(epochMetaKey(height), ((toString(i.originCaller) + ",0,") + toBase16String(blockHash)))] ++ firstBlockId) ++ updateAllBlockIds)
445+ let newChainHeight = (mainChainHeight + 1)
446+ let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(mainChainLastBlock)) + i.originCaller.bytes)
447+[BinaryEntry((blockMetaK + blockHashHex), newBlockMeta), StringEntry(chainMetaKey(mainChainId), ((toString(newChainHeight) + SEP) + blockHashHex)), IntegerEntry(minerChainIdKey(i.originCaller), mainChainId), IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), newChainHeight), thisEpochMeta]
440448 }
441449 else throw("Strict value is not equal to itself.")
442450 }
443451 else throw("Strict value is not equal to itself.")
444452 }
445453 else throw("Strict value is not equal to itself.")
446454 }
447455 else throw("Strict value is not equal to itself.")
448456 }
449457
450458
451459
452460 @Callable(i)
453-func startAltChain (blockHash,reference,epoch) = {
461+func startAltChain (blockHashHex,referenceHex,epoch) = {
454462 let checkEpoch = ensureCorrectEpoch(epoch)
455463 if ((checkEpoch == checkEpoch))
456464 then {
457- let checkReference = ensureReferenceIsSafeForRollback(reference)
458- if ((checkReference == checkReference))
465+ let checkGenerator = ensureMiningEpoch(i.originCaller)
466+ if ((checkGenerator == checkGenerator))
459467 then {
460- let checkGenerator = ensureMiningEpoch(i.originCaller)
461- if ((checkGenerator == checkGenerator))
468+ let $t01198112059 = blockMeta(referenceHex)
469+ let refChainHeight = $t01198112059._1
470+ let refEpoch = $t01198112059._2
471+ let refRef = $t01198112059._3
472+ let refGenerator = $t01198112059._4
473+ let epochRef = if ((refEpoch >= finalizedEpoch))
474+ then refEpoch
475+ else throw((((("can not start alt chain from epoch " + toString(refEpoch)) + ", epoch ") + toString(finalizedEpoch)) + " is finalized"))
476+ let checkChain = ensureExpectedOrInactiveChain(i.originCaller, mainChainId, referenceHex)
477+ if ((checkChain == checkChain))
462478 then {
463- let checkChain = ensureMainOrInactiveChain(i.originCaller)
464- if ((checkChain == checkChain))
465- then {
466- let newChainId = (valueOrElse(getInteger(lastChainIdKey), 0) + 1)
467- let newHeight = (blockMeta(reference)._1 + 1)
468- let newBlockMeta = (((toBytes(newHeight) + toBytes(height)) + reference) + i.originCaller.bytes)
469-[BinaryEntry(blockMetaKey(blockHash), newBlockMeta), BinaryEntry((blockMetaK + toBase16String(blockHash)), newBlockMeta), BinaryEntry(chainFirstBlockIdKey(newChainId), blockHash), BinaryEntry(chainLastBlockIdKey(newChainId), blockHash), StringEntry(chainAllBlockIdsKey(newChainId, 0), toBase16String(blockHash)), IntegerEntry(chainHeightKey(newChainId), newHeight), StringEntry(chainMetaKey(newChainId), ((toString(newHeight) + SEP) + toBase16String(blockHash))), IntegerEntry(chainIdKey(i.originCaller.bytes), newChainId), setEpochData(i.originCaller), StringEntry(supportersKey(newChainId), toString(i.originCaller)), IntegerEntry(lastEpochBlocksNumberKey, 1), IntegerEntry(lastChainIdKey, newChainId), StringEntry(epochMetaKey(height), ((toString(i.originCaller) + ",0,") + toBase16String(blockHash)))]
470- }
471- else throw("Strict value is not equal to itself.")
479+ let newChainId = (valueOrElse(getInteger(lastChainIdKey), 0) + 1)
480+ let referenceHeight = blockMeta(referenceHex)._1
481+ let newChainHeight = (referenceHeight + 1)
482+ let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(referenceHex)) + i.originCaller.bytes)
483+ let thisEpochMeta = match epochMeta(height) {
484+ case _: Unit =>
485+ StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(epochRef)) + SEP) + blockHashHex))
486+ case other =>
487+ throw("Epoch already started")
488+ }
489+[thisEpochMeta, BinaryEntry((blockMetaK + blockHashHex), newBlockMeta), StringEntry(chainFirstBlockIdKey(newChainId), blockHashHex), StringEntry(chainMetaKey(mainChainId), ((toString(newChainHeight) + SEP) + blockHashHex)), IntegerEntry(minerChainIdKey(i.originCaller), newChainId), IntegerEntry(chainLastHeightKey(newChainId, i.originCaller), newChainHeight), IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), referenceHeight), StringEntry(supportersKey(newChainId), toString(i.originCaller)), IntegerEntry(lastChainIdKey, newChainId)]
472490 }
473491 else throw("Strict value is not equal to itself.")
474492 }
475493 else throw("Strict value is not equal to itself.")
476494 }
477495 else throw("Strict value is not equal to itself.")
478496 }
479497
480498
481499
482500 @Callable(i)
483-func extendAltChain (chainId,blockHash,reference,epoch) = {
501+func extendAltChain (chainId,blockHashHex,referenceHex,epoch) = {
484502 let checkEpoch = ensureCorrectEpoch(epoch)
485503 if ((checkEpoch == checkEpoch))
486504 then {
487505 let checkGenerator = ensureMiningEpoch(i.originCaller)
488506 if ((checkGenerator == checkGenerator))
489507 then {
490- let checkChain = ensureAltChainExtension(i.originCaller, chainId)
508+ let checkChain = ensureExpectedOrInactiveChain(i.originCaller, chainId, toBase16String(blockMeta(getStringValue(chainFirstBlockIdKey(chainId)))._3))
491509 if ((checkChain == checkChain))
492510 then {
493- let checkReference = isReferenceCorrect(reference, getBinary(chainLastBlockIdKey(chainId)))
511+ let $t01415914213 = chainMeta(chainId)
512+ let chainHeight = $t01415914213._1
513+ let chainLastBlock = $t01415914213._2
514+ let checkReference = isReferenceCorrect(referenceHex, chainLastBlock)
494515 if ((checkReference == checkReference))
495516 then {
496- let chainHeight = valueOrElse(getInteger(chainHeightKey(chainId)), 0)
497- let blockIdsLastN = valueOrElse(getInteger(chainAllBlockIdsLastNKey(chainId)), 0)
498- let blockIds = getStringValue(chainAllBlockIdsKey(chainId, blockIdsLastN))
499- let newHeight = (chainHeight + 1)
500- let updateAllBlockIds = if ((size(blockIds) >= MAX_BLOCKS_STR_SIZE))
501- then [IntegerEntry(chainAllBlockIdsLastNKey(chainId), (blockIdsLastN + 1)), StringEntry(chainAllBlockIdsKey(chainId, (blockIdsLastN + 1)), toBase16String(blockHash))]
502- else {
503- let newBlockIds = ((toBase16String(blockHash) + SEP) + blockIds)
504-[StringEntry(chainAllBlockIdsKey(chainId, blockIdsLastN), newBlockIds)]
505- }
517+ let newChainHeight = (chainHeight + 1)
506518 let mainChainEntry = if ((supportingBalance(chainId) > (computedTotalBalance / 2)))
507- then [IntegerEntry(mainChainIdKey, chainId)]
519+ then [IntegerEntry(mainChainIdKey, chainId), IntegerEntry(firstValidAltChainIdKey, (valueOrElse(getInteger(lastChainIdKey), 0) + 1))]
508520 else nil
509- let newBlockMeta = (((toBytes(newHeight) + toBytes(height)) + reference) + i.originCaller.bytes)
510- ((([BinaryEntry(blockMetaKey(blockHash), newBlockMeta), BinaryEntry((blockMetaK + toBase16String(blockHash)), newBlockMeta), BinaryEntry(chainLastBlockIdKey(chainId), blockHash), IntegerEntry(chainHeightKey(chainId), newHeight), StringEntry(chainMetaKey(chainId), ((toString(newHeight) + SEP) + toBase16String(blockHash))), setEpochData(i.originCaller), IntegerEntry(chainIdKey(i.originCaller.bytes), chainId), IntegerEntry(lastEpochBlocksNumberKey, 1), StringEntry(epochMetaKey(height), ((toString(i.originCaller) + ",0,") + toBase16String(blockHash)))] ++ mainChainEntry) ++ addSupporter(chainId, i.originCaller)) ++ updateAllBlockIds)
521+ let thisEpochMeta = match epochMeta(height) {
522+ case _: Unit =>
523+ StringEntry(epochMetaKey(height), ((((toString(i.originCaller) + SEP) + toString(blockMeta(referenceHex)._2)) + SEP) + blockHashHex))
524+ case other =>
525+ throw("Epoch already started")
526+ }
527+ let updateMainChainLastMinedBlock = if (if ((mainChainEntry == nil))
528+ then (valueOrElse(minerChainId(i.originCaller), 0) != chainId)
529+ else false)
530+ then [IntegerEntry(chainLastHeightKey(mainChainId, i.originCaller), blockMeta(getStringValue(chainFirstBlockIdKey(chainId)))._1)]
531+ else nil
532+ let newBlockMeta = (((toBytes(newChainHeight) + toBytes(height)) + fromBase16String(referenceHex)) + i.originCaller.bytes)
533+ ((([BinaryEntry((blockMetaK + blockHashHex), newBlockMeta), StringEntry(chainMetaKey(mainChainId), ((toString(newChainHeight) + SEP) + blockHashHex)), thisEpochMeta, IntegerEntry(minerChainIdKey(i.originCaller), chainId), IntegerEntry(chainLastHeightKey(chainId, i.originCaller), newChainHeight)] ++ mainChainEntry) ++ addSupporter(chainId, i.originCaller)) ++ updateMainChainLastMinedBlock)
511534 }
512535 else throw("Strict value is not equal to itself.")
513536 }
514537 else throw("Strict value is not equal to itself.")
515538 }
516539 else throw("Strict value is not equal to itself.")
517540 }
518541 else throw("Strict value is not equal to itself.")
519542 }
520543
521544
522545
523546 @Callable(i)
524547 func join (rewardAddress) = {
525548 func checkRewardAddress (address) = match getBinary(minerPkKey(address)) {
526549 case pk: ByteVector =>
527550 if ((pk == i.originCallerPublicKey))
528551 then unit
529552 else throw(((("L2 miner address " + address) + " is already linked with ") + toBase58String(pk)))
530553 case _ =>
531554 unit
532555 }
533556
534- if ((MIN_BALANCE > wavesBalance(i.originCaller).generating))
535- then throw(((("Insufficient generating balance: " + toString(wavesBalance(i.originCaller).generating)) + ". Required: ") + toString(MIN_BALANCE)))
536- else if ((size(rewardAddress) != 20))
537- then throw("rewardAddress should be an L2 address")
538- else {
539- func checkExistence (exists,miner) = if (exists)
540- then true
541- else (miner == toString(i.originCaller))
557+ if (!(isContractSetup()))
558+ then throw("The contract has not yet set up")
559+ else if ((MIN_BALANCE > wavesBalance(i.originCaller).generating))
560+ then throw(((("Insufficient generating balance: " + toString(wavesBalance(i.originCaller).generating)) + ". Required: ") + toString(MIN_BALANCE)))
561+ else if ((size(rewardAddress) != 20))
562+ then throw("rewardAddress should be an L2 address")
563+ else {
564+ func checkExistence (exists,miner) = if (exists)
565+ then true
566+ else (miner == toString(i.originCaller))
542567
543- let alreadyExists = {
544- let $l = allMiners
545- let $s = size($l)
546- let $acc0 = false
547- func $f0_1 ($a,$i) = if (($i >= $s))
548- then $a
549- else checkExistence($a, $l[$i])
568+ let alreadyExists = {
569+ let $l = allMiners
570+ let $s = size($l)
571+ let $acc0 = false
572+ func $f0_1 ($a,$i) = if (($i >= $s))
573+ then $a
574+ else checkExistence($a, $l[$i])
550575
551- func $f0_2 ($a,$i) = if (($i >= $s))
552- then $a
553- else throw("List size exceeds 50")
576+ func $f0_2 ($a,$i) = if (($i >= $s))
577+ then $a
578+ else throw("List size exceeds 50")
554579
555- $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)
580+ $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)
581+ }
582+ if (alreadyExists)
583+ then nil
584+ else {
585+ let newMiner = toString(i.originCaller)
586+ let rewardAddressHex = toBase16String(rewardAddress)
587+ let check = checkRewardAddress(rewardAddressHex)
588+ if ((check == check))
589+ then {
590+ let newMiners = if ((size(allMiners) == 0))
591+ then newMiner
592+ else ((allMinersStr + SEP) + newMiner)
593+ let deleteOldRewardAddressPk = match getBinary(minerRewardAddressKey(newMiner)) {
594+ case oldAddress: ByteVector =>
595+ if ((oldAddress == rewardAddress))
596+ then nil
597+ else [DeleteEntry(minerPkKey(toBase16String(oldAddress)))]
598+ case _ =>
599+ nil
600+ }
601+ ([StringEntry(allMinersKey, newMiners), StringEntry(minerRewardAddressKey(newMiner), ("0x" + rewardAddressHex)), BinaryEntry(minerPkKey(rewardAddressHex), i.originCallerPublicKey)] ++ deleteOldRewardAddressPk)
602+ }
603+ else throw("Strict value is not equal to itself.")
604+ }
556605 }
557- if (alreadyExists)
558- then nil
559- else {
560- let newMiner = toString(i.originCaller)
561- let rewardAddressHex = toBase16String(rewardAddress)
562- let check = checkRewardAddress(rewardAddressHex)
563- if ((check == check))
564- then {
565- func filterLeaving (acc,miner) = {
566- let $t01829418326 = acc
567- let accMiners = $t01829418326._1
568- let isUpdated = $t01829418326._2
569- if ((miner == newMiner))
570- then $Tuple2(accMiners, true)
571- else $Tuple2((accMiners :+ miner), isUpdated)
572- }
573-
574- let newMiners = if ((size(allMiners) == 0))
575- then newMiner
576- else ((makeString_2C(allMiners, SEP) + SEP) + newMiner)
577- let $t01859018680 = {
578- let $l = allLeavingMiners
579- let $s = size($l)
580- let $acc0 = $Tuple2(nil, false)
581- func $f1_1 ($a,$i) = if (($i >= $s))
582- then $a
583- else filterLeaving($a, $l[$i])
584-
585- func $f1_2 ($a,$i) = if (($i >= $s))
586- then $a
587- else throw("List size exceeds 50")
588-
589- $f1_2($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_1($f1_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)
590- }
591- let newLeavingMiners = $t01859018680._1
592- let isUpdated = $t01859018680._2
593- let newLeavingMinersAction = if ((newLeavingMiners != nil))
594- then [StringEntry(leavingMinersKey(height), makeString_2C(newLeavingMiners, SEP))]
595- else if ((allLeavingMiners != nil))
596- then [DeleteEntry(leavingMinersKey(height))]
597- else nil
598- let joinHeight = if (isUpdated)
599- then height
600- else (height + 1)
601- let deleteOldRewardAddressPk = match getBinary(minerRewardAddress(newMiner)) {
602- case oldAddress: ByteVector =>
603- if ((oldAddress == rewardAddress))
604- then nil
605- else [DeleteEntry(minerPkKey(toBase16String(oldAddress)))]
606- case _ =>
607- nil
608- }
609- (([StringEntry(allMinersKey, newMiners), IntegerEntry(minerJoinHeightKey(newMiner), joinHeight), BinaryEntry(minerRewardAddress(newMiner), rewardAddress), BinaryEntry(minerPkKey(rewardAddressHex), i.originCallerPublicKey)] ++ newLeavingMinersAction) ++ deleteOldRewardAddressPk)
610- }
611- else throw("Strict value is not equal to itself.")
612- }
613- }
614606 }
615607
616608
617609
618610 @Callable(i)
619611 func leave () = {
620612 let leavingMiner = toString(i.originCaller)
621- func filterMiner (acc,miner) = if ((miner == leavingMiner))
613+ func skipLeavingMiner (acc,miner) = if ((miner == leavingMiner))
622614 then acc
623615 else (acc :+ miner)
624616
625- let restMiners = {
617+ let remainingMiners = {
626618 let $l = allMiners
627619 let $s = size($l)
628620 let $acc0 = nil
629621 func $f0_1 ($a,$i) = if (($i >= $s))
630622 then $a
631- else filterMiner($a, $l[$i])
623+ else skipLeavingMiner($a, $l[$i])
632624
633625 func $f0_2 ($a,$i) = if (($i >= $s))
634626 then $a
635627 else throw("List size exceeds 50")
636628
637629 $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)
638630 }
639- if ((thisEpochMiner == i.originCaller.bytes))
631+ let rewardAddrKey = minerRewardAddressKey(leavingMiner)
632+ let prevRewardAddress = valueOrErrorMessage(getBinary(this, rewardAddrKey), "miner has never joined")
633+ if ((thisEpochMiner == i.originCaller))
640634 then throw("designated miner can't leave")
641- else match getInteger(minerJoinHeightKey(leavingMiner)) {
642- case minerHeight: Int =>
643- if ((minerHeight > height))
644- then [StringEntry(allMinersKey, makeString_2C(restMiners, SEP)), DeleteEntry(minerJoinHeightKey(leavingMiner))]
645- else {
646- let oldLeavingMiners = valueOrElse(getString(leavingMinersKey(height)), "")
647- let newLeavingMiners = if ((oldLeavingMiners == ""))
648- then leavingMiner
649- else ((oldLeavingMiners + SEP) + leavingMiner)
650-[StringEntry(allMinersKey, makeString_2C(restMiners, SEP)), DeleteEntry(minerJoinHeightKey(leavingMiner)), StringEntry(leavingMinersKey(height), newLeavingMiners)]
651- }
652- case _ =>
653-[StringEntry(allMinersKey, makeString_2C(restMiners, SEP))]
635+ else [StringEntry(allMinersKey, makeString_2C(remainingMiners, SEP)), DeleteEntry(rewardAddrKey), DeleteEntry(minerPkKey(toBase16String(prevRewardAddress)))]
636+ }
637+
638+
639+
640+@Callable(i)
641+func setup (genesisBlockHashHex,minerReward,stakingContractAddressB58) = if (isContractSetup())
642+ then throw("The contract has been already set up")
643+ else {
644+ let genesisBlockHash = fromBase16String(genesisBlockHashHex)
645+ let emptyPk = base58'11111111111111111111111111111111'
646+ let genesisMinerAddress = addressFromPublicKey(emptyPk)
647+ let genesisEthRewardAddress = base58'11111111111111111111'
648+ let genesisBlockReferenceHash = base58'11111111111111111111111111111111'
649+ let genesisBlockMeta = (((toBytes(0) + toBytes(height)) + genesisBlockReferenceHash) + genesisMinerAddress.bytes)
650+[BinaryEntry((blockMetaK + genesisBlockHashHex), genesisBlockMeta), BinaryEntry(chainFirstBlockIdKey(0), genesisBlockHash), StringEntry(chainMetaKey(0), ("0," + genesisBlockHashHex)), IntegerEntry(minerRewardKey, minerReward), StringEntry(stakingContractAddressKey, stakingContractAddressB58), StringEntry(epochMetaKey(height), ((toString(genesisMinerAddress) + ",0,") + genesisBlockHashHex))]
654651 }
655- }
656652
657653

github/deemru/w8io/6500d08 
150.37 ms