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:
Old | New | Differences | |
---|---|---|---|
7 | 7 | ||
8 | 8 | let MIN_BALANCE = (20000 * WAVES) | |
9 | 9 | ||
10 | - | let MAX_BLOCKS_AT_EPOCH = 50 | |
11 | - | ||
12 | 10 | let SEP = "," | |
13 | 11 | ||
14 | 12 | let BLOCK_HASH_SIZE = 32 | |
15 | 13 | ||
16 | 14 | let ADDRESS_SIZE = 26 | |
17 | - | ||
18 | - | let MAX_BLOCKS_STR_SIZE = ((64 * MAX_BLOCKS_AT_EPOCH) + (MAX_BLOCKS_AT_EPOCH - 1)) | |
19 | 15 | ||
20 | 16 | let thisEpochDataKey = "thisEpochData" | |
21 | 17 | ||
25 | 21 | ||
26 | 22 | let lastChainIdKey = "lastChainId" | |
27 | 23 | ||
28 | - | let | |
24 | + | let firstValidAltChainIdKey = "firstValidAltChainId" | |
29 | 25 | ||
30 | 26 | let minerRewardKey = "minerReward" | |
27 | + | ||
28 | + | let stakingContractAddressKey = "stakingContractAddress" | |
31 | 29 | ||
32 | 30 | let blockMetaK = "block_0x" | |
33 | 31 | ||
57 | 55 | func epochMetaKey (epoch) = ("epoch_" + pad(epoch)) | |
58 | 56 | ||
59 | 57 | ||
58 | + | func chainFirstBlockIdKey (chainId) = (("chain" + toString(chainId)) + "FirstBlock") | |
59 | + | ||
60 | + | ||
60 | 61 | func chainMetaKey (chainId) = ("chain_" + pad(chainId)) | |
61 | 62 | ||
62 | 63 | ||
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 | + | } | |
64 | 85 | ||
65 | 86 | func generatingBalance (address) = match getString(stakingContractAddress, ("%s__" + toString(address))) { | |
66 | 87 | case str: String => | |
79 | 100 | } | |
80 | 101 | ||
81 | 102 | ||
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 | + | } | |
116 | 108 | ||
117 | 109 | ||
118 | 110 | let mainChainId = valueOrElse(getInteger(mainChainIdKey), 0) | |
119 | 111 | ||
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]) | |
127 | 122 | case _ => | |
128 | 123 | unit | |
129 | 124 | } | |
130 | 125 | ||
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 | |
134 | 130 | 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 | + | } | |
136 | 141 | } | |
137 | 142 | ||
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 { | |
141 | 152 | 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") | |
143 | 161 | } | |
144 | 162 | ||
145 | 163 | 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) | |
152 | 168 | let blockGenerator = takeRight(meta, ADDRESS_SIZE) | |
153 | 169 | $Tuple4(blockHeight, blockEpoch, blockParent, blockGenerator) | |
154 | 170 | } | |
155 | 171 | ||
156 | 172 | ||
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 | + | } | |
158 | 180 | ||
159 | 181 | ||
160 | - | let $ | |
182 | + | let $t043025268 = { | |
161 | 183 | let hitSource = match lastBlock.vrf { | |
162 | 184 | case vrf: ByteVector => | |
163 | 185 | vrf | |
165 | 187 | lastBlock.generationSignature | |
166 | 188 | } | |
167 | 189 | func processMiner (prev,miner) = { | |
168 | - | let $ | |
169 | - | let prevDelay = $ | |
170 | - | let prevMiner = $ | |
171 | - | let prevTotalBalance = $ | |
172 | - | let prevMiners = $ | |
190 | + | let $t046004663 = prev | |
191 | + | let prevDelay = $t046004663._1 | |
192 | + | let prevMiner = $t046004663._2 | |
193 | + | let prevTotalBalance = $t046004663._3 | |
194 | + | let prevMiners = $t046004663._4 | |
173 | 195 | let minerAddress = addressFromStringValue(miner) | |
174 | 196 | let wavesGenBalance = wavesBalance(minerAddress).generating | |
175 | 197 | let minerBalance = generatingBalance(minerAddress) | |
176 | - | | |
198 | + | if (if ((MIN_BALANCE > wavesGenBalance)) | |
177 | 199 | then true | |
178 | 200 | else (0 >= minerBalance)) | |
179 | - | then true | |
180 | - | else (valueOrElse(getInteger(minerJoinHeightKey(miner)), INT_MAX) > height)) | |
181 | 201 | then prev | |
182 | 202 | else { | |
183 | 203 | let nextDelay = calculateDelay(minerAddress, minerBalance) | |
187 | 207 | } | |
188 | 208 | } | |
189 | 209 | ||
190 | - | let $l = | |
210 | + | let $l = allMiners | |
191 | 211 | let $s = size($l) | |
192 | 212 | let $acc0 = $Tuple4(INT_MAX, "", 0, nil) | |
193 | 213 | func $f0_1 ($a,$i) = if (($i >= $s)) | |
201 | 221 | $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) | |
202 | 222 | } | |
203 | 223 | ||
204 | - | let computedDelay = $ | |
224 | + | let computedDelay = $t043025268._1 | |
205 | 225 | ||
206 | - | let computedGenerator = $ | |
226 | + | let computedGenerator = $t043025268._2 | |
207 | 227 | ||
208 | - | let computedTotalBalance = $ | |
228 | + | let computedTotalBalance = $t043025268._3 | |
209 | 229 | ||
210 | - | let filteredMiners = $ | |
230 | + | let filteredMiners = $t043025268._4 | |
211 | 231 | ||
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 | + | } | |
230 | 271 | } | |
231 | 272 | ||
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 | |
243 | 275 | let $s = size($l) | |
244 | - | let $acc0 = $ | |
276 | + | let $acc0 = $Tuple4(mainChainEpoch, 0, unit, nil) | |
245 | 277 | func $f0_1 ($a,$i) = if (($i >= $s)) | |
246 | 278 | then $a | |
247 | - | else | |
279 | + | else step($a, $l[$i]) | |
248 | 280 | ||
249 | 281 | func $f0_2 ($a,$i) = if (($i >= $s)) | |
250 | 282 | then $a | |
252 | 284 | ||
253 | 285 | $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) | |
254 | 286 | } | |
255 | - | let _b = $t061146188._1 | |
256 | - | let blockId = $t061146188._2 | |
257 | - | blockMeta(blockId)._1 | |
287 | + | valueOrElse(res._3, $Tuple2(1, "")) | |
258 | 288 | } | |
289 | + | ||
290 | + | let finalizedEpoch = $t054386554._1 | |
291 | + | ||
292 | + | let finalizedBlockHash = $t054386554._2 | |
259 | 293 | ||
260 | 294 | func supportingBalance (chainId) = { | |
261 | 295 | func addBalance (acc,generatorStr) = { | |
262 | - | let $ | |
263 | - | let totalBalance = $ | |
264 | - | let generators = $ | |
296 | + | let $t066726708 = acc | |
297 | + | let totalBalance = $t066726708._1 | |
298 | + | let generators = $t066726708._2 | |
265 | 299 | let generator = addressFromStringValue(generatorStr) | |
266 | 300 | if (containsElement(generators, generator)) | |
267 | 301 | then acc | |
272 | 306 | } | |
273 | 307 | ||
274 | 308 | let allGenerators = split_4C(getStringValue(supportersKey(chainId)), SEP) | |
275 | - | let $ | |
309 | + | let $t070307095 = { | |
276 | 310 | let $l = allGenerators | |
277 | 311 | let $s = size($l) | |
278 | 312 | let $acc0 = $Tuple2(0, nil) | |
286 | 320 | ||
287 | 321 | $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) | |
288 | 322 | } | |
289 | - | let balance = $ | |
290 | - | let _g = $ | |
323 | + | let balance = $t070307095._1 | |
324 | + | let _g = $t070307095._2 | |
291 | 325 | balance | |
292 | 326 | } | |
327 | + | ||
328 | + | ||
329 | + | func isContractSetup () = isDefined(getInteger(minerRewardKey)) | |
293 | 330 | ||
294 | 331 | ||
295 | 332 | func ensureMiningEpoch (generator) = if ((toString(generator) != computedGenerator)) | |
297 | 334 | else unit | |
298 | 335 | ||
299 | 336 | ||
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)) | |
310 | 340 | ||
311 | 341 | ||
312 | 342 | func chainIsInactive (chainId) = { | |
313 | 343 | 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) | |
315 | 348 | } | |
316 | 349 | ||
317 | 350 | ||
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)))) | |
328 | 352 | ||
329 | 353 | ||
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 | + | } | |
353 | 374 | } | |
354 | 375 | ||
355 | 376 | ||
356 | 377 | func ensureCorrectEpoch (epoch) = if ((epoch == height)) | |
357 | 378 | then unit | |
358 | 379 | 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") | |
364 | 380 | ||
365 | 381 | ||
366 | 382 | func addSupporter (chainId,generator) = { | |
372 | 388 | } | |
373 | 389 | ||
374 | 390 | ||
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 | + | ||
375 | 399 | @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))] | |
399 | 419 | } | |
400 | - | else throw("Strict value is not equal to itself.") | |
401 | - | } | |
420 | + | else throw("Strict value is not equal to itself.") | |
421 | + | } | |
402 | 422 | ||
403 | 423 | ||
404 | 424 | ||
405 | 425 | @Callable(i) | |
406 | - | func extendMainChain ( | |
426 | + | func extendMainChain (blockHashHex,referenceHex,epoch) = { | |
407 | 427 | let checkEpoch = ensureCorrectEpoch(epoch) | |
408 | 428 | if ((checkEpoch == checkEpoch)) | |
409 | 429 | then { | |
410 | 430 | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
411 | 431 | if ((checkGenerator == checkGenerator)) | |
412 | 432 | then { | |
413 | - | let checkChain = | |
433 | + | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, mainChainId, unit) | |
414 | 434 | if ((checkChain == checkChain)) | |
415 | 435 | 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) | |
419 | 437 | if ((checkReference == checkReference)) | |
420 | 438 | 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") | |
437 | 444 | } | |
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] | |
440 | 448 | } | |
441 | 449 | else throw("Strict value is not equal to itself.") | |
442 | 450 | } | |
450 | 458 | ||
451 | 459 | ||
452 | 460 | @Callable(i) | |
453 | - | func startAltChain ( | |
461 | + | func startAltChain (blockHashHex,referenceHex,epoch) = { | |
454 | 462 | let checkEpoch = ensureCorrectEpoch(epoch) | |
455 | 463 | if ((checkEpoch == checkEpoch)) | |
456 | 464 | then { | |
457 | - | let | |
458 | - | if (( | |
465 | + | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
466 | + | if ((checkGenerator == checkGenerator)) | |
459 | 467 | 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)) | |
462 | 478 | 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)] | |
472 | 490 | } | |
473 | 491 | else throw("Strict value is not equal to itself.") | |
474 | 492 | } | |
480 | 498 | ||
481 | 499 | ||
482 | 500 | @Callable(i) | |
483 | - | func extendAltChain (chainId, | |
501 | + | func extendAltChain (chainId,blockHashHex,referenceHex,epoch) = { | |
484 | 502 | let checkEpoch = ensureCorrectEpoch(epoch) | |
485 | 503 | if ((checkEpoch == checkEpoch)) | |
486 | 504 | then { | |
487 | 505 | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
488 | 506 | if ((checkGenerator == checkGenerator)) | |
489 | 507 | then { | |
490 | - | let checkChain = | |
508 | + | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, chainId, toBase16String(blockMeta(getStringValue(chainFirstBlockIdKey(chainId)))._3)) | |
491 | 509 | if ((checkChain == checkChain)) | |
492 | 510 | 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) | |
494 | 515 | if ((checkReference == checkReference)) | |
495 | 516 | 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) | |
506 | 518 | let mainChainEntry = if ((supportingBalance(chainId) > (computedTotalBalance / 2))) | |
507 | - | then [IntegerEntry(mainChainIdKey, chainId)] | |
519 | + | then [IntegerEntry(mainChainIdKey, chainId), IntegerEntry(firstValidAltChainIdKey, (valueOrElse(getInteger(lastChainIdKey), 0) + 1))] | |
508 | 520 | 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) | |
511 | 534 | } | |
512 | 535 | else throw("Strict value is not equal to itself.") | |
513 | 536 | } | |
531 | 554 | unit | |
532 | 555 | } | |
533 | 556 | ||
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)) | |
542 | 567 | ||
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]) | |
550 | 575 | ||
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") | |
554 | 579 | ||
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 | + | } | |
556 | 605 | } | |
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 | - | } | |
614 | 606 | } | |
615 | 607 | ||
616 | 608 | ||
618 | 610 | @Callable(i) | |
619 | 611 | func leave () = { | |
620 | 612 | let leavingMiner = toString(i.originCaller) | |
621 | - | func | |
613 | + | func skipLeavingMiner (acc,miner) = if ((miner == leavingMiner)) | |
622 | 614 | then acc | |
623 | 615 | else (acc :+ miner) | |
624 | 616 | ||
625 | - | let | |
617 | + | let remainingMiners = { | |
626 | 618 | let $l = allMiners | |
627 | 619 | let $s = size($l) | |
628 | 620 | let $acc0 = nil | |
629 | 621 | func $f0_1 ($a,$i) = if (($i >= $s)) | |
630 | 622 | then $a | |
631 | - | else | |
623 | + | else skipLeavingMiner($a, $l[$i]) | |
632 | 624 | ||
633 | 625 | func $f0_2 ($a,$i) = if (($i >= $s)) | |
634 | 626 | then $a | |
636 | 628 | ||
637 | 629 | $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) | |
638 | 630 | } | |
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)) | |
640 | 634 | 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))] | |
654 | 651 | } | |
655 | - | } | |
656 | 652 | ||
657 | 653 |
Old | New | Differences | |
---|---|---|---|
1 | 1 | {-# STDLIB_VERSION 8 #-} | |
2 | 2 | {-# SCRIPT_TYPE ACCOUNT #-} | |
3 | 3 | {-# CONTENT_TYPE DAPP #-} | |
4 | 4 | let INT_MAX = 9223372036854775807 | |
5 | 5 | ||
6 | 6 | let WAVES = 100000000 | |
7 | 7 | ||
8 | 8 | let MIN_BALANCE = (20000 * WAVES) | |
9 | 9 | ||
10 | - | let MAX_BLOCKS_AT_EPOCH = 50 | |
11 | - | ||
12 | 10 | let SEP = "," | |
13 | 11 | ||
14 | 12 | let BLOCK_HASH_SIZE = 32 | |
15 | 13 | ||
16 | 14 | let ADDRESS_SIZE = 26 | |
17 | - | ||
18 | - | let MAX_BLOCKS_STR_SIZE = ((64 * MAX_BLOCKS_AT_EPOCH) + (MAX_BLOCKS_AT_EPOCH - 1)) | |
19 | 15 | ||
20 | 16 | let thisEpochDataKey = "thisEpochData" | |
21 | 17 | ||
22 | 18 | let allMinersKey = "allMiners" | |
23 | 19 | ||
24 | 20 | let mainChainIdKey = "mainChainId" | |
25 | 21 | ||
26 | 22 | let lastChainIdKey = "lastChainId" | |
27 | 23 | ||
28 | - | let | |
24 | + | let firstValidAltChainIdKey = "firstValidAltChainId" | |
29 | 25 | ||
30 | 26 | let minerRewardKey = "minerReward" | |
27 | + | ||
28 | + | let stakingContractAddressKey = "stakingContractAddress" | |
31 | 29 | ||
32 | 30 | let blockMetaK = "block_0x" | |
33 | 31 | ||
34 | 32 | func pad (i) = { | |
35 | 33 | let s = toString(i) | |
36 | 34 | match size(s) { | |
37 | 35 | case _ => | |
38 | 36 | if ((1 == $match0)) | |
39 | 37 | then ("0000000" + s) | |
40 | 38 | else if ((2 == $match0)) | |
41 | 39 | then ("000000" + s) | |
42 | 40 | else if ((3 == $match0)) | |
43 | 41 | then ("00000" + s) | |
44 | 42 | else if ((4 == $match0)) | |
45 | 43 | then ("0000" + s) | |
46 | 44 | else if ((5 == $match0)) | |
47 | 45 | then ("000" + s) | |
48 | 46 | else if ((6 == $match0)) | |
49 | 47 | then ("00" + s) | |
50 | 48 | else if ((7 == $match0)) | |
51 | 49 | then ("0" + s) | |
52 | 50 | else s | |
53 | 51 | } | |
54 | 52 | } | |
55 | 53 | ||
56 | 54 | ||
57 | 55 | func epochMetaKey (epoch) = ("epoch_" + pad(epoch)) | |
58 | 56 | ||
59 | 57 | ||
58 | + | func chainFirstBlockIdKey (chainId) = (("chain" + toString(chainId)) + "FirstBlock") | |
59 | + | ||
60 | + | ||
60 | 61 | func chainMetaKey (chainId) = ("chain_" + pad(chainId)) | |
61 | 62 | ||
62 | 63 | ||
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 | + | } | |
64 | 85 | ||
65 | 86 | func generatingBalance (address) = match getString(stakingContractAddress, ("%s__" + toString(address))) { | |
66 | 87 | case str: String => | |
67 | 88 | let paramList = split(str, "__") | |
68 | 89 | let prevHeight = parseIntValue(paramList[1]) | |
69 | 90 | let prevBalance = parseIntValue(paramList[2]) | |
70 | 91 | let nextHeight = parseIntValue(paramList[3]) | |
71 | 92 | let nextBalance = parseIntValue(paramList[4]) | |
72 | 93 | if ((height >= nextHeight)) | |
73 | 94 | then nextBalance | |
74 | 95 | else if ((height >= prevHeight)) | |
75 | 96 | then prevBalance | |
76 | 97 | else 0 | |
77 | 98 | case _ => | |
78 | 99 | 0 | |
79 | 100 | } | |
80 | 101 | ||
81 | 102 | ||
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 | + | } | |
116 | 108 | ||
117 | 109 | ||
118 | 110 | let mainChainId = valueOrElse(getInteger(mainChainIdKey), 0) | |
119 | 111 | ||
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]) | |
127 | 122 | case _ => | |
128 | 123 | unit | |
129 | 124 | } | |
130 | 125 | ||
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 | |
134 | 130 | 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 | + | } | |
136 | 141 | } | |
137 | 142 | ||
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 { | |
141 | 152 | 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") | |
143 | 161 | } | |
144 | 162 | ||
145 | 163 | 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) | |
152 | 168 | let blockGenerator = takeRight(meta, ADDRESS_SIZE) | |
153 | 169 | $Tuple4(blockHeight, blockEpoch, blockParent, blockGenerator) | |
154 | 170 | } | |
155 | 171 | ||
156 | 172 | ||
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 | + | } | |
158 | 180 | ||
159 | 181 | ||
160 | - | let $ | |
182 | + | let $t043025268 = { | |
161 | 183 | let hitSource = match lastBlock.vrf { | |
162 | 184 | case vrf: ByteVector => | |
163 | 185 | vrf | |
164 | 186 | case _ => | |
165 | 187 | lastBlock.generationSignature | |
166 | 188 | } | |
167 | 189 | func processMiner (prev,miner) = { | |
168 | - | let $ | |
169 | - | let prevDelay = $ | |
170 | - | let prevMiner = $ | |
171 | - | let prevTotalBalance = $ | |
172 | - | let prevMiners = $ | |
190 | + | let $t046004663 = prev | |
191 | + | let prevDelay = $t046004663._1 | |
192 | + | let prevMiner = $t046004663._2 | |
193 | + | let prevTotalBalance = $t046004663._3 | |
194 | + | let prevMiners = $t046004663._4 | |
173 | 195 | let minerAddress = addressFromStringValue(miner) | |
174 | 196 | let wavesGenBalance = wavesBalance(minerAddress).generating | |
175 | 197 | let minerBalance = generatingBalance(minerAddress) | |
176 | - | | |
198 | + | if (if ((MIN_BALANCE > wavesGenBalance)) | |
177 | 199 | then true | |
178 | 200 | else (0 >= minerBalance)) | |
179 | - | then true | |
180 | - | else (valueOrElse(getInteger(minerJoinHeightKey(miner)), INT_MAX) > height)) | |
181 | 201 | then prev | |
182 | 202 | else { | |
183 | 203 | let nextDelay = calculateDelay(minerAddress, minerBalance) | |
184 | 204 | if ((prevDelay > nextDelay)) | |
185 | 205 | then $Tuple4(nextDelay, miner, (prevTotalBalance + minerBalance), (prevMiners :+ miner)) | |
186 | 206 | else $Tuple4(prevDelay, prevMiner, (prevTotalBalance + minerBalance), (prevMiners :+ miner)) | |
187 | 207 | } | |
188 | 208 | } | |
189 | 209 | ||
190 | - | let $l = | |
210 | + | let $l = allMiners | |
191 | 211 | let $s = size($l) | |
192 | 212 | let $acc0 = $Tuple4(INT_MAX, "", 0, nil) | |
193 | 213 | func $f0_1 ($a,$i) = if (($i >= $s)) | |
194 | 214 | then $a | |
195 | 215 | else processMiner($a, $l[$i]) | |
196 | 216 | ||
197 | 217 | func $f0_2 ($a,$i) = if (($i >= $s)) | |
198 | 218 | then $a | |
199 | 219 | else throw("List size exceeds 50") | |
200 | 220 | ||
201 | 221 | $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) | |
202 | 222 | } | |
203 | 223 | ||
204 | - | let computedDelay = $ | |
224 | + | let computedDelay = $t043025268._1 | |
205 | 225 | ||
206 | - | let computedGenerator = $ | |
226 | + | let computedGenerator = $t043025268._2 | |
207 | 227 | ||
208 | - | let computedTotalBalance = $ | |
228 | + | let computedTotalBalance = $t043025268._3 | |
209 | 229 | ||
210 | - | let filteredMiners = $ | |
230 | + | let filteredMiners = $t043025268._4 | |
211 | 231 | ||
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 | + | } | |
230 | 271 | } | |
231 | 272 | ||
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 | |
243 | 275 | let $s = size($l) | |
244 | - | let $acc0 = $ | |
276 | + | let $acc0 = $Tuple4(mainChainEpoch, 0, unit, nil) | |
245 | 277 | func $f0_1 ($a,$i) = if (($i >= $s)) | |
246 | 278 | then $a | |
247 | - | else | |
279 | + | else step($a, $l[$i]) | |
248 | 280 | ||
249 | 281 | func $f0_2 ($a,$i) = if (($i >= $s)) | |
250 | 282 | then $a | |
251 | 283 | else throw("List size exceeds 200") | |
252 | 284 | ||
253 | 285 | $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) | |
254 | 286 | } | |
255 | - | let _b = $t061146188._1 | |
256 | - | let blockId = $t061146188._2 | |
257 | - | blockMeta(blockId)._1 | |
287 | + | valueOrElse(res._3, $Tuple2(1, "")) | |
258 | 288 | } | |
289 | + | ||
290 | + | let finalizedEpoch = $t054386554._1 | |
291 | + | ||
292 | + | let finalizedBlockHash = $t054386554._2 | |
259 | 293 | ||
260 | 294 | func supportingBalance (chainId) = { | |
261 | 295 | func addBalance (acc,generatorStr) = { | |
262 | - | let $ | |
263 | - | let totalBalance = $ | |
264 | - | let generators = $ | |
296 | + | let $t066726708 = acc | |
297 | + | let totalBalance = $t066726708._1 | |
298 | + | let generators = $t066726708._2 | |
265 | 299 | let generator = addressFromStringValue(generatorStr) | |
266 | 300 | if (containsElement(generators, generator)) | |
267 | 301 | then acc | |
268 | 302 | else { | |
269 | 303 | let balance = generatingBalance(generator) | |
270 | 304 | $Tuple2((totalBalance + balance), (generators :+ generator)) | |
271 | 305 | } | |
272 | 306 | } | |
273 | 307 | ||
274 | 308 | let allGenerators = split_4C(getStringValue(supportersKey(chainId)), SEP) | |
275 | - | let $ | |
309 | + | let $t070307095 = { | |
276 | 310 | let $l = allGenerators | |
277 | 311 | let $s = size($l) | |
278 | 312 | let $acc0 = $Tuple2(0, nil) | |
279 | 313 | func $f0_1 ($a,$i) = if (($i >= $s)) | |
280 | 314 | then $a | |
281 | 315 | else addBalance($a, $l[$i]) | |
282 | 316 | ||
283 | 317 | func $f0_2 ($a,$i) = if (($i >= $s)) | |
284 | 318 | then $a | |
285 | 319 | else throw("List size exceeds 100") | |
286 | 320 | ||
287 | 321 | $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) | |
288 | 322 | } | |
289 | - | let balance = $ | |
290 | - | let _g = $ | |
323 | + | let balance = $t070307095._1 | |
324 | + | let _g = $t070307095._2 | |
291 | 325 | balance | |
292 | 326 | } | |
327 | + | ||
328 | + | ||
329 | + | func isContractSetup () = isDefined(getInteger(minerRewardKey)) | |
293 | 330 | ||
294 | 331 | ||
295 | 332 | func ensureMiningEpoch (generator) = if ((toString(generator) != computedGenerator)) | |
296 | 333 | then throw(((((toBase58String(generator.bytes) + " is not allowed to mine in ") + toString(height)) + " epoch. Expected ") + computedGenerator)) | |
297 | 334 | else unit | |
298 | 335 | ||
299 | 336 | ||
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)) | |
310 | 340 | ||
311 | 341 | ||
312 | 342 | func chainIsInactive (chainId) = { | |
313 | 343 | 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) | |
315 | 348 | } | |
316 | 349 | ||
317 | 350 | ||
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)))) | |
328 | 352 | ||
329 | 353 | ||
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 | + | } | |
353 | 374 | } | |
354 | 375 | ||
355 | 376 | ||
356 | 377 | func ensureCorrectEpoch (epoch) = if ((epoch == height)) | |
357 | 378 | then unit | |
358 | 379 | 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") | |
364 | 380 | ||
365 | 381 | ||
366 | 382 | func addSupporter (chainId,generator) = { | |
367 | 383 | let supportersStr = getStringValue(supportersKey(chainId)) | |
368 | 384 | let supporters = split(supportersStr, SEP) | |
369 | 385 | if (containsElement(supporters, toString(generator))) | |
370 | 386 | then nil | |
371 | 387 | else [StringEntry(supportersKey(chainId), ((supportersStr + SEP) + toString(generator)))] | |
372 | 388 | } | |
373 | 389 | ||
374 | 390 | ||
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 | + | ||
375 | 399 | @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))] | |
399 | 419 | } | |
400 | - | else throw("Strict value is not equal to itself.") | |
401 | - | } | |
420 | + | else throw("Strict value is not equal to itself.") | |
421 | + | } | |
402 | 422 | ||
403 | 423 | ||
404 | 424 | ||
405 | 425 | @Callable(i) | |
406 | - | func extendMainChain ( | |
426 | + | func extendMainChain (blockHashHex,referenceHex,epoch) = { | |
407 | 427 | let checkEpoch = ensureCorrectEpoch(epoch) | |
408 | 428 | if ((checkEpoch == checkEpoch)) | |
409 | 429 | then { | |
410 | 430 | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
411 | 431 | if ((checkGenerator == checkGenerator)) | |
412 | 432 | then { | |
413 | - | let checkChain = | |
433 | + | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, mainChainId, unit) | |
414 | 434 | if ((checkChain == checkChain)) | |
415 | 435 | 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) | |
419 | 437 | if ((checkReference == checkReference)) | |
420 | 438 | 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") | |
437 | 444 | } | |
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] | |
440 | 448 | } | |
441 | 449 | else throw("Strict value is not equal to itself.") | |
442 | 450 | } | |
443 | 451 | else throw("Strict value is not equal to itself.") | |
444 | 452 | } | |
445 | 453 | else throw("Strict value is not equal to itself.") | |
446 | 454 | } | |
447 | 455 | else throw("Strict value is not equal to itself.") | |
448 | 456 | } | |
449 | 457 | ||
450 | 458 | ||
451 | 459 | ||
452 | 460 | @Callable(i) | |
453 | - | func startAltChain ( | |
461 | + | func startAltChain (blockHashHex,referenceHex,epoch) = { | |
454 | 462 | let checkEpoch = ensureCorrectEpoch(epoch) | |
455 | 463 | if ((checkEpoch == checkEpoch)) | |
456 | 464 | then { | |
457 | - | let | |
458 | - | if (( | |
465 | + | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
466 | + | if ((checkGenerator == checkGenerator)) | |
459 | 467 | 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)) | |
462 | 478 | 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)] | |
472 | 490 | } | |
473 | 491 | else throw("Strict value is not equal to itself.") | |
474 | 492 | } | |
475 | 493 | else throw("Strict value is not equal to itself.") | |
476 | 494 | } | |
477 | 495 | else throw("Strict value is not equal to itself.") | |
478 | 496 | } | |
479 | 497 | ||
480 | 498 | ||
481 | 499 | ||
482 | 500 | @Callable(i) | |
483 | - | func extendAltChain (chainId, | |
501 | + | func extendAltChain (chainId,blockHashHex,referenceHex,epoch) = { | |
484 | 502 | let checkEpoch = ensureCorrectEpoch(epoch) | |
485 | 503 | if ((checkEpoch == checkEpoch)) | |
486 | 504 | then { | |
487 | 505 | let checkGenerator = ensureMiningEpoch(i.originCaller) | |
488 | 506 | if ((checkGenerator == checkGenerator)) | |
489 | 507 | then { | |
490 | - | let checkChain = | |
508 | + | let checkChain = ensureExpectedOrInactiveChain(i.originCaller, chainId, toBase16String(blockMeta(getStringValue(chainFirstBlockIdKey(chainId)))._3)) | |
491 | 509 | if ((checkChain == checkChain)) | |
492 | 510 | 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) | |
494 | 515 | if ((checkReference == checkReference)) | |
495 | 516 | 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) | |
506 | 518 | let mainChainEntry = if ((supportingBalance(chainId) > (computedTotalBalance / 2))) | |
507 | - | then [IntegerEntry(mainChainIdKey, chainId)] | |
519 | + | then [IntegerEntry(mainChainIdKey, chainId), IntegerEntry(firstValidAltChainIdKey, (valueOrElse(getInteger(lastChainIdKey), 0) + 1))] | |
508 | 520 | 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) | |
511 | 534 | } | |
512 | 535 | else throw("Strict value is not equal to itself.") | |
513 | 536 | } | |
514 | 537 | else throw("Strict value is not equal to itself.") | |
515 | 538 | } | |
516 | 539 | else throw("Strict value is not equal to itself.") | |
517 | 540 | } | |
518 | 541 | else throw("Strict value is not equal to itself.") | |
519 | 542 | } | |
520 | 543 | ||
521 | 544 | ||
522 | 545 | ||
523 | 546 | @Callable(i) | |
524 | 547 | func join (rewardAddress) = { | |
525 | 548 | func checkRewardAddress (address) = match getBinary(minerPkKey(address)) { | |
526 | 549 | case pk: ByteVector => | |
527 | 550 | if ((pk == i.originCallerPublicKey)) | |
528 | 551 | then unit | |
529 | 552 | else throw(((("L2 miner address " + address) + " is already linked with ") + toBase58String(pk))) | |
530 | 553 | case _ => | |
531 | 554 | unit | |
532 | 555 | } | |
533 | 556 | ||
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)) | |
542 | 567 | ||
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]) | |
550 | 575 | ||
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") | |
554 | 579 | ||
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 | + | } | |
556 | 605 | } | |
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 | - | } | |
614 | 606 | } | |
615 | 607 | ||
616 | 608 | ||
617 | 609 | ||
618 | 610 | @Callable(i) | |
619 | 611 | func leave () = { | |
620 | 612 | let leavingMiner = toString(i.originCaller) | |
621 | - | func | |
613 | + | func skipLeavingMiner (acc,miner) = if ((miner == leavingMiner)) | |
622 | 614 | then acc | |
623 | 615 | else (acc :+ miner) | |
624 | 616 | ||
625 | - | let | |
617 | + | let remainingMiners = { | |
626 | 618 | let $l = allMiners | |
627 | 619 | let $s = size($l) | |
628 | 620 | let $acc0 = nil | |
629 | 621 | func $f0_1 ($a,$i) = if (($i >= $s)) | |
630 | 622 | then $a | |
631 | - | else | |
623 | + | else skipLeavingMiner($a, $l[$i]) | |
632 | 624 | ||
633 | 625 | func $f0_2 ($a,$i) = if (($i >= $s)) | |
634 | 626 | then $a | |
635 | 627 | else throw("List size exceeds 50") | |
636 | 628 | ||
637 | 629 | $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) | |
638 | 630 | } | |
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)) | |
640 | 634 | 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))] | |
654 | 651 | } | |
655 | - | } | |
656 | 652 | ||
657 | 653 |
github/deemru/w8io/169f3d6 101.03 ms ◑![]()