Browse Source

Merge pull request #4932 from SomberNight/revealer_cleanup_20181215

revealer: clean-up, allow restoring v0
3.3.3.1
ghost43 6 years ago
committed by GitHub
parent
commit
8e5331e5b2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      electrum/gui/qt/util.py
  2. 136
      electrum/plugins/revealer/qt.py
  3. 105
      electrum/plugins/revealer/revealer.py
  4. 36
      electrum/tests/test_revealer.py

2
electrum/gui/qt/util.py

@ -216,7 +216,7 @@ class MessageBoxMixin(object):
d = QMessageBox(icon, title, str(text), buttons, parent)
d.setWindowModality(Qt.WindowModal)
d.setDefaultButton(defaultButton)
d.setTextInteractionFlags(Qt.TextSelectableByMouse)
d.setTextInteractionFlags(Qt.TextSelectableByMouse | Qt.LinksAccessibleByMouse)
return d.exec_()
class WindowModalDialog(QDialog, MessageBoxMixin):

136
electrum/plugins/revealer/qt.py

@ -13,27 +13,26 @@ import os
import random
import qrcode
import traceback
from hashlib import sha256
from decimal import Decimal
import binascii
from PyQt5.QtPrintSupport import QPrinter
from electrum.plugin import BasePlugin, hook
from electrum.plugin import hook
from electrum.i18n import _
from electrum.util import to_bytes, make_dir, InvalidPassword, UserCancelled
from electrum.util import make_dir, InvalidPassword, UserCancelled, bh2u, bfh
from electrum.gui.qt.util import *
from electrum.gui.qt.qrtextedit import ScanQRTextEdit
from electrum.gui.qt.main_window import StatusBarButton
from .hmac_drbg import DRBG
from .revealer import RevealerPlugin, VersionedSeed
class Plugin(BasePlugin):
class Plugin(RevealerPlugin):
MAX_PLAINTEXT_LEN = 189 # chars
def __init__(self, parent, config, name):
BasePlugin.__init__(self, parent, config, name)
RevealerPlugin.__init__(self, parent, config, name)
self.base_dir = os.path.join(config.electrum_path(), 'revealer')
if self.config.get('calibration_h') is None:
@ -44,8 +43,6 @@ class Plugin(BasePlugin):
self.calibration_h = self.config.get('calibration_h')
self.calibration_v = self.config.get('calibration_v')
self.version = '1'
self.size = (159, 97)
self.f_size = QSize(1014*2, 642*2)
self.abstand_h = 21
self.abstand_v = 34
@ -90,7 +87,6 @@ class Plugin(BasePlugin):
self.wallet = window.parent().wallet
self.update_wallet_name(self.wallet)
self.user_input = False
self.noise_seed = False
self.d = WindowModalDialog(window, "Setup Dialog")
self.d.setMinimumWidth(500)
@ -145,39 +141,12 @@ class Plugin(BasePlugin):
return ''.join(text.split()).lower()
def on_edit(self):
s = self.get_noise()
b = self.is_noise(s)
if b:
self.noise_seed = s[1:-3]
self.user_input = True
self.next_button.setEnabled(b)
def code_hashid(self, txt):
x = to_bytes(txt, 'utf8')
hash = sha256(x).hexdigest()
return hash[-3:].upper()
def is_noise(self, txt):
if (len(txt) >= 34):
try:
int(txt, 16)
except:
self.user_input = False
return False
else:
id = self.code_hashid(txt[:-3])
if (txt[-3:].upper() == id.upper()):
self.code_id = id
self.user_input = True
return True
else:
return False
else:
if (len(txt)>0 and txt[0]=='0'):
self.d.show_message(''.join(["<b>",_("Warning: "), "</b>", _("Revealers starting with 0 had a vulnerability and are not supported.")]))
self.user_input = False
return False
txt = self.get_noise()
versioned_seed = self.get_versioned_seed_from_user_input(txt)
if versioned_seed:
self.versioned_seed = versioned_seed
self.user_input = bool(versioned_seed)
self.next_button.setEnabled(bool(versioned_seed))
def make_digital(self, dialog):
self.make_rawnoise(True)
@ -185,7 +154,9 @@ class Plugin(BasePlugin):
self.d.close()
def get_path_to_revealer_file(self, ext: str= '') -> str:
filename = self.filename_prefix + self.version + "_" + self.code_id + ext
version = self.versioned_seed.version
code_id = self.versioned_seed.checksum
filename = self.filename_prefix + version + "_" + code_id + ext
path = os.path.join(self.base_dir, filename)
return os.path.normcase(os.path.abspath(path))
@ -195,7 +166,9 @@ class Plugin(BasePlugin):
def bcrypt(self, dialog):
self.rawnoise = False
dialog.show_message(''.join([_("{} encrypted for Revealer {}_{} saved as PNG and PDF at: ").format(self.was, self.version, self.code_id),
version = self.versioned_seed.version
code_id = self.versioned_seed.checksum
dialog.show_message(''.join([_("{} encrypted for Revealer {}_{} saved as PNG and PDF at: ").format(self.was, version, code_id),
"<b>", self.get_path_to_revealer_file(), "</b>", "<br/>",
"<br/>", "<b>", _("Always check you backups.")]))
dialog.close()
@ -206,7 +179,9 @@ class Plugin(BasePlugin):
dialog.close()
def bdone(self, dialog):
dialog.show_message(''.join([_("Digital Revealer ({}_{}) saved as PNG and PDF at:").format(self.version, self.code_id),
version = self.versioned_seed.version
code_id = self.versioned_seed.checksum
dialog.show_message(''.join([_("Digital Revealer ({}_{}) saved as PNG and PDF at:").format(version, code_id),
"<br/>","<b>", self.get_path_to_revealer_file(), '</b>']))
@ -224,7 +199,19 @@ class Plugin(BasePlugin):
self.txt = self.text.text()
self.seed_img(is_seed=False)
def warn_old_revealer(self):
if self.versioned_seed.version == '0':
link = "https://revealer.cc/revealer-warning-and-upgrade/"
self.d.show_warning(("<b>{warning}: </b>{ver0}<br>"
"{url}<br>"
"{risk}")
.format(warning=_("Warning"),
ver0=_("Revealers starting with 0 are not secure due to a vulnerability."),
url=_("More info at: {}").format(f'<a href="{link}">{link}</a>'),
risk=_("Proceed at your own risk.")))
def cypherseed_dialog(self, window):
self.warn_old_revealer()
d = WindowModalDialog(window, "Encryption Dialog")
d.setMinimumWidth(500)
@ -241,7 +228,8 @@ class Plugin(BasePlugin):
logo.setAlignment(Qt.AlignLeft)
hbox.addSpacing(16)
self.vbox.addWidget(WWLabel("<b>" + _("Revealer Secret Backup Plugin") + "</b><br>"
+ _("Ready to encrypt for revealer {}").format(self.version+'_'+self.code_id )))
+ _("Ready to encrypt for revealer {}")
.format(self.versioned_seed.version+'_'+self.versioned_seed.checksum)))
self.vbox.addSpacing(11)
hbox.addLayout(self.vbox)
grid = QGridLayout()
@ -294,7 +282,7 @@ class Plugin(BasePlugin):
else:
txt = self.txt.upper()
img = QImage(self.size[0],self.size[1], QImage.Format_Mono)
img = QImage(self.SIZE[0], self.SIZE[1], QImage.Format_Mono)
bitmap = QBitmap.fromImage(img, Qt.MonoOnly)
bitmap.fill(Qt.white)
painter = QPainter()
@ -325,7 +313,7 @@ class Plugin(BasePlugin):
while len(' '.join(map(str, temp_seed))) > max_letters:
nwords = nwords - 1
temp_seed = seed_array[:nwords]
painter.drawText(QRect(0, linespace*n , self.size[0], self.size[1]), Qt.AlignHCenter, ' '.join(map(str, temp_seed)))
painter.drawText(QRect(0, linespace*n , self.SIZE[0], self.SIZE[1]), Qt.AlignHCenter, ' '.join(map(str, temp_seed)))
del seed_array[:nwords]
painter.end()
@ -337,43 +325,23 @@ class Plugin(BasePlugin):
return img
def make_rawnoise(self, create_revealer=False):
w = self.size[0]
h = self.size[1]
if not self.user_input:
self.versioned_seed = self.gen_random_versioned_seed()
assert self.versioned_seed
w, h = self.SIZE
rawnoise = QImage(w, h, QImage.Format_Mono)
if(self.noise_seed == False):
self.noise_seed = random.SystemRandom().getrandbits(128)
self.hex_noise = format(self.noise_seed, '032x')
self.hex_noise = self.version + str(self.hex_noise)
if (self.user_input == True):
self.noise_seed = int(self.noise_seed, 16)
self.hex_noise = self.version + str(format(self.noise_seed, '032x'))
self.code_id = self.code_hashid(self.hex_noise)
self.hex_noise = ' '.join(self.hex_noise[i:i+4] for i in range(0,len(self.hex_noise),4))
entropy = binascii.unhexlify(str(format(self.noise_seed, '032x')))
code_id = binascii.unhexlify(self.version + self.code_id)
drbg = DRBG(entropy + code_id)
noise_array=bin(int.from_bytes(drbg.generate(1929), 'big'))[2:]
i=0
for x in range(w):
for y in range(h):
rawnoise.setPixel(x,y,int(noise_array[i]))
i+=1
noise_map = self.get_noise_map(self.versioned_seed)
for (x,y), pixel in noise_map.items():
rawnoise.setPixel(x, y, pixel)
self.rawnoise = rawnoise
if create_revealer==True:
if create_revealer:
self.make_revealer()
self.noise_seed = False
def make_calnoise(self):
random.seed(self.calibration_noise)
w = self.size[0]
h = self.size[1]
w, h = self.SIZE
rawnoise = QImage(w, h, QImage.Format_Mono)
for x in range(w):
for y in range(h):
@ -422,7 +390,7 @@ class Plugin(BasePlugin):
return cypherseed
def calibration(self):
img = QImage(self.size[0],self.size[1], QImage.Format_Mono)
img = QImage(self.SIZE[0], self.SIZE[1], QImage.Format_Mono)
bitmap = QBitmap.fromImage(img, Qt.MonoOnly)
bitmap.fill(Qt.black)
self.make_calnoise()
@ -586,7 +554,8 @@ class Plugin(BasePlugin):
(base_img.height()-((total_distance_h)))-((border_thick*8)/2)-(border_thick/2)-2)
painter.setPen(QColor(0,0,0,255))
painter.drawText(QRect(0, base_img.height()-107, base_img.width()-total_distance_h - border_thick - 11,
base_img.height()-total_distance_h - border_thick), Qt.AlignRight, self.version + '_'+self.code_id)
base_img.height()-total_distance_h - border_thick), Qt.AlignRight,
self.versioned_seed.version + '_'+self.versioned_seed.checksum)
painter.end()
else: # revealer
@ -635,12 +604,13 @@ class Plugin(BasePlugin):
painter.setPen(QColor(0,0,0,255))
painter.drawText(QRect(((base_img.width()/2) +21)-qr_size, base_img.height()-107,
base_img.width()-total_distance_h - border_thick -93,
base_img.height()-total_distance_h - border_thick), Qt.AlignLeft, self.hex_noise.upper())
base_img.height()-total_distance_h - border_thick), Qt.AlignLeft, self.versioned_seed.get_ui_string_version_plus_seed())
painter.drawText(QRect(0, base_img.height()-107, base_img.width()-total_distance_h - border_thick -3 -qr_size,
base_img.height()-total_distance_h - border_thick), Qt.AlignRight, self.code_id)
base_img.height()-total_distance_h - border_thick), Qt.AlignRight, self.versioned_seed.checksum)
# draw qr code
qr_qt = self.paintQR(self.hex_noise.upper() +self.code_id)
qr_qt = self.paintQR(self.versioned_seed.get_ui_string_version_plus_seed()
+ self.versioned_seed.checksum)
target = QRectF(base_img.width()-65-qr_size,
base_img.height()-65-qr_size,
qr_size, qr_size )

105
electrum/plugins/revealer/revealer.py

@ -0,0 +1,105 @@
import random
import os
from hashlib import sha256
from typing import NamedTuple, Optional, Dict, Tuple
from electrum.plugin import BasePlugin
from electrum.util import to_bytes, bh2u, bfh
from .hmac_drbg import DRBG
class VersionedSeed(NamedTuple):
version: str
seed: str
checksum: str
def get_ui_string_version_plus_seed(self):
version, seed = self.version, self.seed
assert isinstance(version, str) and len(version) == 1, version
assert isinstance(seed, str) and len(seed) >= 32
ret = version + seed
ret = ret.upper()
return ' '.join(ret[i : i+4] for i in range(0, len(ret), 4))
class RevealerPlugin(BasePlugin):
LATEST_VERSION = '1'
KNOWN_VERSIONS = ('0', '1')
assert LATEST_VERSION in KNOWN_VERSIONS
SIZE = (159, 97)
def __init__(self, parent, config, name):
BasePlugin.__init__(self, parent, config, name)
@classmethod
def code_hashid(cls, txt: str) -> str:
txt = txt.lower()
x = to_bytes(txt, 'utf8')
hash = sha256(x).hexdigest()
return hash[-3:].upper()
@classmethod
def get_versioned_seed_from_user_input(cls, txt: str) -> Optional[VersionedSeed]:
if len(txt) < 34:
return None
try:
int(txt, 16)
except:
return None
version = txt[0]
if version not in cls.KNOWN_VERSIONS:
return None
checksum = cls.code_hashid(txt[:-3])
if txt[-3:].upper() != checksum.upper():
return None
return VersionedSeed(version=version.upper(),
seed=txt[1:-3].upper(),
checksum=checksum.upper())
@classmethod
def get_noise_map(cls, versioned_seed: VersionedSeed) -> Dict[Tuple[int, int], int]:
"""Returns a map from (x,y) coordinate to pixel value 0/1, to be used as rawnoise."""
w, h = cls.SIZE
version = versioned_seed.version
hex_seed = versioned_seed.seed
checksum = versioned_seed.checksum
noise_map = {}
if version == '0':
random.seed(int(hex_seed, 16))
for x in range(w):
for y in range(h):
noise_map[(x, y)] = random.randint(0, 1)
elif version == '1':
prng_seed = bfh(hex_seed + version + checksum)
drbg = DRBG(prng_seed)
num_noise_bytes = 1929 # ~ w*h
noise_array = bin(int.from_bytes(drbg.generate(num_noise_bytes), 'big'))[2:]
# there's an approx 1/1024 chance that the generated number is 'too small'
# and we would get IndexError below. easiest backwards compat fix:
noise_array += '0' * (w * h - len(noise_array))
i = 0
for x in range(w):
for y in range(h):
noise_map[(x, y)] = int(noise_array[i])
i += 1
else:
raise Exception(f"unexpected revealer version: {version}")
return noise_map
@classmethod
def gen_random_versioned_seed(cls):
version = cls.LATEST_VERSION
hex_seed = bh2u(os.urandom(16))
checksum = cls.code_hashid(version + hex_seed)
return VersionedSeed(version=version.upper(),
seed=hex_seed.upper(),
checksum=checksum.upper())
if __name__ == '__main__':
for i in range(10**4):
vs = RevealerPlugin.gen_random_versioned_seed()
nm = RevealerPlugin.get_noise_map(vs)

36
electrum/tests/test_revealer.py

@ -0,0 +1,36 @@
from electrum.plugins.revealer.revealer import RevealerPlugin
from . import SequentialTestCase
class TestRevealer(SequentialTestCase):
def test_version_0_noisemap(self):
versioned_seed = RevealerPlugin.get_versioned_seed_from_user_input('03b0c557d6d0d4308a3393851d78bd8c7861')
noise_map = RevealerPlugin.get_noise_map(versioned_seed)
bigint = 0
for (x, y), pixel in noise_map.items():
if pixel:
bigint |= 1 << (y*RevealerPlugin.SIZE[1]+x)
self.assertEqual(0x541dde00b20ac7d320510e943d7ed9ffff5ff6b431c915353fbeffbc1beb737ff3a59c032a39ff8cbd532dffe42655bccbbef4f777ffeff8ec90e64aacbff5f4ff37ef4f32ac1d7240ed2bbb37dfeff459c7c2e2e0bfddffff7fffc7fd27eeb84a5ceafcf6bf9ffaff632367f97fffbf9fbfecff2b3a11a1c5befdfbfe7f125fba2c3e5d4ded591f9fbbbadbeed2220fb4337df9e4c7bfbe6ce4ad7b18ad57f3d75dfe7b6deb7350478bdbf7b7bfdf776eb301217d1f5c7f7ffffeefffe2070f52dbedfff2fef7f27f7d27f80b6a7bfb7f67bcbf7faf11f6b577dfbefebd44ffffe7bf5ee17ba4fb3377e1fcffeded781eca37a5bff3ebefdccbe1538c16129aed7fadfd7eb3bab55bcbdaee7e5d5b9fff57bd662333923b27af4f4da5ffd8bb15b58effed8bbeeff9ab7ecb75b62b977fdd88f3fbaeef6997a999b4dfffbfa375bf9e9c12b6011e2fde9fef7f66efc1155cc4fedfeefffeeff6ded645712b12bfe2b35df796f7ca05e0f12afbff6fefd1dd7f736bb9a567dff5797eafc1bfa0cf6cd090ddddfbfb79fd9f7f17bba2197e5dd3fb7fd9ff7579f0b6e28f7df3bdfe6fa8efd5a0a2e48f4d6efff79bf5efebc2638ff7eefffbbdfdb5bfac80426052df6fe6fd33eff5336a3c87c9fcff797b6bddbf91fea62e635333ffd7bfdd35f5c365432f5dfe7fd8bfb6c6e7cc90e6b5796d1dfeef567fdf390124a4bfeefd7efd1eee7f88ca45658fbf5cabffbfebf9fefe2c9f73dbffd36d7df77d73665c5f1dfa7b5b7fffafb6ea18bf9396e37b77fffffffb6aefdb0635ff7e43cfeef77fd8a527741dd3fffef1eddedf7f4259cc4253ffd9dffffb3bfbb0632d3d7fbf7bfbbfedfff2a7589be7624faffffbdbd7dfb5b5189b66fde8abf8bfbba7f440b80c2ba86de5fdfdf7ffba25625877fb9fdbf6f39333fe20e7710cdffadef8e7fb727d059237ef3dfceb9bd7ffef7b041565f2bb7ffdfefffff1ba3c7abb9a0fcf3fdf78fee7efb5da83dd1ffadebde675ff36d725426027dfffd9f76df3f7605b7f6fa4f75ff6e3df57ffd1a56c6239feffebffeecffdbd1ecc69f99ffea7f5ec759e2b2a99977b467edbffafae5faef9e719b7bff73fe9fed753ddc20ba23d8e7fdf4adfdbefadbb6a6775f7ef7eddffdfffeead7be0b38dcefeeb6afffef3d272d1b0492e733fff15dc3bfa2bfb83b9fefbfdf853fbddfbdbdf354868fd6dedf93edffca29130013bfcfbe27f4feffc86bbaa925bffdeed3fede76b321dc0abb57fe367df5adaaf30cc615c1efef7fbffe3993e583ff3721bdfbe66edef8faef24697f311ff6ff57ffefbebff9b90325bda76f77daeabfbcb9abd45c0bffe576bc3fffffc96911d477ddbbc3feef7f63a4510ec1265e6e1fe765eaafbca10400876bff7bffdfdffcc9920f60119beedfffd57e6ff383e6c3637def9fdffb7bfffb3339f94eb3fdf5bd7bbdfdf621d8f008dad195dffd6ffbff57a1ce166e5ef9f85febdde28a4a013987bf7ffebffffb56cf7aa522589bdafdf51ff4f39ed386097667fafdbfffff7ef9379dbc136bedfb9ff7aefffc3f081be97bf4e7ecfbde35cea3018d1bf1bffbfaeebefb9fac072f05bac77f7fdffeffe2eb1bd4d90a6fddafd7c2ff7bf9ba80d6f6df77ce727ff9a97fb41f03dcfbc557b3fbcc80b,
bigint)
def test_version_1_noisemap(self):
versioned_seed = RevealerPlugin.get_versioned_seed_from_user_input('125Df05b7ccf079ce2978Ae18e99219868cd')
noise_map = RevealerPlugin.get_noise_map(versioned_seed)
bigint = 0
for (x, y), pixel in noise_map.items():
if pixel:
bigint |= 1 << (y*RevealerPlugin.SIZE[1]+x)
self.assertEqual(0x36fde1eece10b3f674227ea76f7ababbcbf87dfba1eddf2edebfeefec3dffff719ee1cbd477b9be7cf6fcdaff924ff05a26ff2fb7bfdbbdef1a2f90c097d7cdadfbb9d1ef592c27c85efffffff7ffc8dff60d6de87f71c9fe77f7f5372cfdb1dc0eb9e959dbed42197c7ee4288f7fbf73b65fdfbd5e153ede49bb957edaefe6f7dee4a72502eef77babfa7fff7d0a3fc6f5ffefb3b7b67aab66118a56eb1fffe1ddafbeefefa96b26d715bb8e5fafbbb2ffcf64e8df2bdffeffed39ffdfdef986491a7fbf97bffb7fafee072640b7af6edf8da2f7cdff268ccd52b75f53f9afef77b4be4db9c5f9debffeff5f7f1f7b1882cb4eed67f757b37ebf0b2c7f849bd73f4737f3ffb5a3f75ac537ff5fff8edcdfeb6be63d3147ffeefa9caf7ebf740989520c1eddedabdfd73f7f821fc3977fdfbe9fbee7d6e8ca9f16b8b8f1decbffef17f806ade988d77fef5775cff3f7bd9759675f4773fff6fefaff385fe807fdbfcbbffefa6d7c4ed54a0d1959cefeecffdffe8cd539451dfdfbff71ff7e97cf37aec8069efffcf7536ebbc515e991cf293ff97feffd72cebe110d44bf787f1efecda7306ac88cd49dfff257cbd9ff7ff8fd1686eedf2dfeb373fbe2a10ed81f7d9c979316efceeea745926377ffcffba7edf67fc79cace0eeef5ff5ceffeeeff94d20c4dadd53efee6f97bfed2f8ae059ff7ffbfedfbf17f2d45bc8afdebf1dff7856ebd02c39ae22ef7befc9e97cbb7f31af5bd57e3feffafc4d7fef9ae222e9fdcf5a2dfbffefd50399d7d095d22fd7bf9fdef6d5d5044bafffdfd57fff7cbe6af91096b3f5fffe3fff7fb97fa930d316db6dfbf236ddbc8abb3bbea6edf9deafd39b8efac7ae014e7ffbdd97cfebe7ec84a72a7b323fe77afffd7f1c8eaec48e6ff3fdf7da9fffaf2d57e961d7f5debc9afe7ff32d72ff374d3ff57fff2fbffdbe9833405fcbabff8beb8ff1e55f53b2d6e96bdf7e7fbfd0fb6b130071c13cf5de5be5ef8ade46a2dd53d77ff69eff7ff946ad4e32febddff87b73fceeaf2ce94adafbb9fddeff8fbf11f4161ad6cb29dbe5f2ffe6ee2023ceeb79c76d7fff7bbffaf4485b6f6f3b7f97f9f75ce372c173177fedd65e5fb76fdc5bbf7a737f9bdddefefff1f7a5533dd1efde7ffafabcd96e1193e3cadb93e76fcdfdb4fa533bc3d3a7eff5eebc9f3ffce91aa51bbf5e7f6bcfd7dfbfd1928c0726f3f26ef8f3ffe2eb8843cbb1dfd2eabfe4f7ffec47a95263e5c65737affe73e3e3735d61e8cbffdbf75e37fc04a991ad7ff7feffa6fafdfed988b50fdf379ffdfff2f7eeb6738c0ceffff77f32fe7f2b22c866514db75f7c3df6e5fd210a70bb4bbf31bcfb6d325f4a00b06ed7d34c9dbbdffff3fd6fd8ed570d7def1dcf789ff5ae040339ff35deb5e37bedf889d83bcf5feffb77e57ffcfdd8edfd91bb8bffd3b7dbd8fea3083734c3d7dedd9ffefcb78c5d87e1919f5655bf5bf1bfb3dd65fb64ee2fffd777afef18965d03872d73bbfb6fa7df7250f3e8d6ef7ff9ffffeadff5e39abf8727fde93febddfffee3096ca1779ceabbf7ff7bda2f756353be9dfabf2bcff6feec1cad233fffbf9ecefffffa21b7b8b17ffded7ff7fef56ee44b02d9bdf3d3cf42aa777fd90ba9b08af4cfd5b797dadb3694bf3282abcb39fb2d760f9,
bigint)
def test_version_1_noisemap_indexerror(self):
versioned_seed = RevealerPlugin.get_versioned_seed_from_user_input('1A082CBDC627FFA37ABD154A64AD2565D725')
noise_map = RevealerPlugin.get_noise_map(versioned_seed)
bigint = 0
for (x, y), pixel in noise_map.items():
if pixel:
bigint |= 1 << (y*RevealerPlugin.SIZE[1]+x)
self.assertEqual(0x20bdba94d80b604107d92e4bb77fbdff66cbd769d14d31cc26fdbb77fffff237db49c1cb6bf5cfd4fbb7e169dfc0213b57ffbf7e7fb3dffbdce1f92bbb595efffbefeeb06e00cd6fbfcf5572e6d4f376843fed920475dfdad7dbfffedf1f5fb9de7f67ff77aefefb753daebce8eeb77ff7e35eaeefaf48fe37c7fd71ecfa96faf6d49bd1b60e29cbff7ff2f5fe7daa83d231efdfdfc2ffd17887d9aa79b1713f16dfcf75f7fa1e64c574b7abbea7abfff9c0e27b9f55cbdedffbff3eff95508796eb8bbf9dfffc367f9b6b1b59337ffebf3ff9fecdffd61a6febbc3aafff379dbafcfc814c56bfaffdd1fb7ffeb35a1c293e9f393e35b5d70fbeca50020427db9ecabaf3fff9a2211ee279fdffb53973fbbefa0d2d9270fe2fef5ffbaffefd753cc251ebff7ff352e3f77fdd50bd3975ee3ffcdff6f5fe199284436d7adfbffbbfaf9efabde086fe79bfdf21fb7efba8079b7eae57ffbabff97b3e7463300e53ff5fd5f295ffe7340062f84f689dadfd3ff13feaef33d83ef7fa9aea65fff1f53021f5baef9ffddfbfbf5dff0ad38328f03ebfadfcffdffdc45e2a9a1ef64fbe4f1b7f7fb567039baafffdffed87f7bb8784f28e7dffd7d7fbfff2e7c0d13cf0d7ff77baffefbf73f6fe851607ef9fb5bf7fd5e8fe0d07d6e9bf7de7effd3b7cbd8e55336e7fef8fcbc67fb9a74ba6481d7dfdc6f3fedd3ffe87e7184216dcffd7dfef7b7a6024b2c4d5ffc72d99d7cd5ff6da6717ecf2ffffceffb6e686a4fb4d57fbcebf7ef9b3ae4c5a553340bafff7f7377bffbe3dae1fb43b738ac7ebd5dfed0c8537fe4b0ddc277b7ffbeff63c618bebcdffe9bdff6f9e52be359a5feac691ffbcdf6abb62102b09fa66f9ffafd4b7f73d88a81e3ff2be4b7f7feba2a2566203f7af53ef7253ebf64ac799ff9e7dcff7fadadbffd23fc9b2fd9efecaeaffcff04efa1e0866fe1f9379ffaed4b9792af1b63fffceed58fcff21bac450bb7ffd9aebeffef5d6525510a55b5ffffef78f7bfa1c94ff7f3d7fcbe3df7d3fbc5a0f710157befdeff762edf2bcfed7fb7ef96ff17dd6bddb9dcf01126dfee1f5b33bf1ef3c4c21c13ffbfbfd985fbfd3804539d95ba5def9dfcdceb7b17c6bf29df6dbefbe7fb9fd9f4fd39a8fb4727b87ee579d88721c9bb3f7ffffb7ffd9df369daca81d57d5ffd0bfdeffaf1434ffcb5b6af7f7eddebdbe403b7d4abfec6f719f6b9fee205da43d79fdef7c7dbfdef2f0f61b98fb39efb43cf7eeee082d36101abb73d9effbbf77dc474f6f47fbbebb1f36b76df0dffa846efedbb7affdfbf77222153dff7fdd7dffff5e81f3b5a8ee07ff3bdfe9b77ef1fecabd51c4ef272feffebeefea7334e38d7fc5effce7ffaccffdd18506bee13bffddefbd4f092fbf6e57dddfbfd7fefdf78303790e7b1befc77f6bbcbf9348c44b37fbee7feebffb57217373b0febef97effeb29ff79daad6df17f7fedfde7eeff727c6b54217fcfbb56ffeeffbc492064ad04cff6ff9b7c2efe308364110873875bff4bb73f5c2666500afbdbbc75ff9bebc5be4465eafbbffb7ff7b7fdc8bb7dee0747df7fcfd76a4bfe5e698aa3f37fe2bffbffb65b780150337efb7a21fdfb747d14fd95ceefd7f7cfe0f6db3c0ca5eb52fb7fe82ff7f7acdea3f9ddabf48ceec4ce41bb13,
bigint)
Loading…
Cancel
Save