I have a contract where I'd like to convert a hash (sha256 in this case, but I'm open to any of the other hashing algorithms available) into an Int (or Nat) between 1 and 100.
I have the code that generates a hash, but it doesn't seem possible to convert the hash bytes into a Nat (or Int) in order to divide or modulo to constrain it to a range.
Trying to modulo the hash directly throws a type error. Here's what I have so far:
import smartpy as sp
class HashToNat(sp.Contract):
def __init__(self):
self.init(
rnd = sp.none,
rnd_nat = 0
)
@sp.entry_point
def do_it(self):
# Do some stuff
self.data.rnd = sp.some(sp.sha256(sp.pack(sp.now)))
# Error: Type Error
# sp.TBytes is not sp.TIntOrNat
self.data.rnd_nat = self.data.rnd.open_some() % 100
@sp.add_test(name = "Amount recording")
def test():
scenario = sp.test_scenario()
scenario.h1("hash to nat?")
c1 = HashToNat()
scenario += c1
scenario += c1.do_it().run()
EDIT: Based on Raphaël Cauderlier's advice below, I've crafted a working example that appears to work. I welcome any feedback/improvements in the code or any caveats to watch out for.
import smartpy as sp
class HashToNat(sp.Contract):
def __init__(self):
self.init(
hash = sp.bytes('0x'),
nat_value = 0,
bytes_to_nat = {
sp.bytes('0x00'): 0, sp.bytes('0x01'): 1,
sp.bytes('0x02'): 2, sp.bytes('0x03'): 3,
sp.bytes('0x04'): 4, sp.bytes('0x05'): 5,
sp.bytes('0x06'): 6, sp.bytes('0x07'): 7,
sp.bytes('0x08'): 8, sp.bytes('0x09'): 9,
sp.bytes('0x0a'): 10, sp.bytes('0x0b'): 11,
sp.bytes('0x0c'): 12, sp.bytes('0x0d'): 13,
sp.bytes('0x0e'): 14, sp.bytes('0x0f'): 15,
sp.bytes('0x10'): 16, sp.bytes('0x11'): 17,
sp.bytes('0x12'): 18, sp.bytes('0x13'): 19,
sp.bytes('0x14'): 20, sp.bytes('0x15'): 21,
sp.bytes('0x16'): 22, sp.bytes('0x17'): 23,
sp.bytes('0x18'): 24, sp.bytes('0x19'): 25,
sp.bytes('0x1a'): 26, sp.bytes('0x1b'): 27,
sp.bytes('0x1c'): 28, sp.bytes('0x1d'): 29,
sp.bytes('0x1e'): 30, sp.bytes('0x1f'): 31,
sp.bytes('0x20'): 32, sp.bytes('0x21'): 33,
sp.bytes('0x22'): 34, sp.bytes('0x23'): 35,
sp.bytes('0x24'): 36, sp.bytes('0x25'): 37,
sp.bytes('0x26'): 38, sp.bytes('0x27'): 39,
sp.bytes('0x28'): 40, sp.bytes('0x29'): 41,
sp.bytes('0x2a'): 42, sp.bytes('0x2b'): 43,
sp.bytes('0x2c'): 44, sp.bytes('0x2d'): 45,
sp.bytes('0x2e'): 46, sp.bytes('0x2f'): 47,
sp.bytes('0x30'): 48, sp.bytes('0x31'): 49,
sp.bytes('0x32'): 50, sp.bytes('0x33'): 51,
sp.bytes('0x34'): 52, sp.bytes('0x35'): 53,
sp.bytes('0x36'): 54, sp.bytes('0x37'): 55,
sp.bytes('0x38'): 56, sp.bytes('0x39'): 57,
sp.bytes('0x3a'): 58, sp.bytes('0x3b'): 59,
sp.bytes('0x3c'): 60, sp.bytes('0x3d'): 61,
sp.bytes('0x3e'): 62, sp.bytes('0x3f'): 63,
sp.bytes('0x40'): 64, sp.bytes('0x41'): 65,
sp.bytes('0x42'): 66, sp.bytes('0x43'): 67,
sp.bytes('0x44'): 68, sp.bytes('0x45'): 69,
sp.bytes('0x46'): 70, sp.bytes('0x47'): 71,
sp.bytes('0x48'): 72, sp.bytes('0x49'): 73,
sp.bytes('0x4a'): 74, sp.bytes('0x4b'): 75,
sp.bytes('0x4c'): 76, sp.bytes('0x4d'): 77,
sp.bytes('0x4e'): 78, sp.bytes('0x4f'): 79,
sp.bytes('0x50'): 80, sp.bytes('0x51'): 81,
sp.bytes('0x52'): 82, sp.bytes('0x53'): 83,
sp.bytes('0x54'): 84, sp.bytes('0x55'): 85,
sp.bytes('0x56'): 86, sp.bytes('0x57'): 87,
sp.bytes('0x58'): 88, sp.bytes('0x59'): 89,
sp.bytes('0x5a'): 90, sp.bytes('0x5b'): 91,
sp.bytes('0x5c'): 92, sp.bytes('0x5d'): 93,
sp.bytes('0x5e'): 94, sp.bytes('0x5f'): 95,
sp.bytes('0x60'): 96, sp.bytes('0x61'): 97,
sp.bytes('0x62'): 98, sp.bytes('0x63'): 99,
sp.bytes('0x64'): 100, sp.bytes('0x65'): 101,
sp.bytes('0x66'): 102, sp.bytes('0x67'): 103,
sp.bytes('0x68'): 104, sp.bytes('0x69'): 105,
sp.bytes('0x6a'): 106, sp.bytes('0x6b'): 107,
sp.bytes('0x6c'): 108, sp.bytes('0x6d'): 109,
sp.bytes('0x6e'): 110, sp.bytes('0x6f'): 111,
sp.bytes('0x70'): 112, sp.bytes('0x71'): 113,
sp.bytes('0x72'): 114, sp.bytes('0x73'): 115,
sp.bytes('0x74'): 116, sp.bytes('0x75'): 117,
sp.bytes('0x76'): 118, sp.bytes('0x77'): 119,
sp.bytes('0x78'): 120, sp.bytes('0x79'): 121,
sp.bytes('0x7a'): 122, sp.bytes('0x7b'): 123,
sp.bytes('0x7c'): 124, sp.bytes('0x7d'): 125,
sp.bytes('0x7e'): 126, sp.bytes('0x7f'): 127,
sp.bytes('0x80'): 128, sp.bytes('0x81'): 129,
sp.bytes('0x82'): 130, sp.bytes('0x83'): 131,
sp.bytes('0x84'): 132, sp.bytes('0x85'): 133,
sp.bytes('0x86'): 134, sp.bytes('0x87'): 135,
sp.bytes('0x88'): 136, sp.bytes('0x89'): 137,
sp.bytes('0x8a'): 138, sp.bytes('0x8b'): 139,
sp.bytes('0x8c'): 140, sp.bytes('0x8d'): 141,
sp.bytes('0x8e'): 142, sp.bytes('0x8f'): 143,
sp.bytes('0x90'): 144, sp.bytes('0x91'): 145,
sp.bytes('0x92'): 146, sp.bytes('0x93'): 147,
sp.bytes('0x94'): 148, sp.bytes('0x95'): 149,
sp.bytes('0x96'): 150, sp.bytes('0x97'): 151,
sp.bytes('0x98'): 152, sp.bytes('0x99'): 153,
sp.bytes('0x9a'): 154, sp.bytes('0x9b'): 155,
sp.bytes('0x9c'): 156, sp.bytes('0x9d'): 157,
sp.bytes('0x9e'): 158, sp.bytes('0x9f'): 159,
sp.bytes('0xa0'): 160, sp.bytes('0xa1'): 161,
sp.bytes('0xa2'): 162, sp.bytes('0xa3'): 163,
sp.bytes('0xa4'): 164, sp.bytes('0xa5'): 165,
sp.bytes('0xa6'): 166, sp.bytes('0xa7'): 167,
sp.bytes('0xa8'): 168, sp.bytes('0xa9'): 169,
sp.bytes('0xaa'): 170, sp.bytes('0xab'): 171,
sp.bytes('0xac'): 172, sp.bytes('0xad'): 173,
sp.bytes('0xae'): 174, sp.bytes('0xaf'): 175,
sp.bytes('0xb0'): 176, sp.bytes('0xb1'): 177,
sp.bytes('0xb2'): 178, sp.bytes('0xb3'): 179,
sp.bytes('0xb4'): 180, sp.bytes('0xb5'): 181,
sp.bytes('0xb6'): 182, sp.bytes('0xb7'): 183,
sp.bytes('0xb8'): 184, sp.bytes('0xb9'): 185,
sp.bytes('0xba'): 186, sp.bytes('0xbb'): 187,
sp.bytes('0xbc'): 188, sp.bytes('0xbd'): 189,
sp.bytes('0xbe'): 190, sp.bytes('0xbf'): 191,
sp.bytes('0xc0'): 192, sp.bytes('0xc1'): 193,
sp.bytes('0xc2'): 194, sp.bytes('0xc3'): 195,
sp.bytes('0xc4'): 196, sp.bytes('0xc5'): 197,
sp.bytes('0xc6'): 198, sp.bytes('0xc7'): 199,
sp.bytes('0xc8'): 200, sp.bytes('0xc9'): 201,
sp.bytes('0xca'): 202, sp.bytes('0xcb'): 203,
sp.bytes('0xcc'): 204, sp.bytes('0xcd'): 205,
sp.bytes('0xce'): 206, sp.bytes('0xcf'): 207,
sp.bytes('0xd0'): 208, sp.bytes('0xd1'): 209,
sp.bytes('0xd2'): 210, sp.bytes('0xd3'): 211,
sp.bytes('0xd4'): 212, sp.bytes('0xd5'): 213,
sp.bytes('0xd6'): 214, sp.bytes('0xd7'): 215,
sp.bytes('0xd8'): 216, sp.bytes('0xd9'): 217,
sp.bytes('0xda'): 218, sp.bytes('0xdb'): 219,
sp.bytes('0xdc'): 220, sp.bytes('0xdd'): 221,
sp.bytes('0xde'): 222, sp.bytes('0xdf'): 223,
sp.bytes('0xe0'): 224, sp.bytes('0xe1'): 225,
sp.bytes('0xe2'): 226, sp.bytes('0xe3'): 227,
sp.bytes('0xe4'): 228, sp.bytes('0xe5'): 229,
sp.bytes('0xe6'): 230, sp.bytes('0xe7'): 231,
sp.bytes('0xe8'): 232, sp.bytes('0xe9'): 233,
sp.bytes('0xea'): 234, sp.bytes('0xeb'): 235,
sp.bytes('0xec'): 236, sp.bytes('0xed'): 237,
sp.bytes('0xee'): 238, sp.bytes('0xef'): 239,
sp.bytes('0xf0'): 240, sp.bytes('0xf1'): 241,
sp.bytes('0xf2'): 242, sp.bytes('0xf3'): 243,
sp.bytes('0xf4'): 244, sp.bytes('0xf5'): 245,
sp.bytes('0xf6'): 246, sp.bytes('0xf7'): 247,
sp.bytes('0xf8'): 248, sp.bytes('0xf9'): 249,
sp.bytes('0xfa'): 250, sp.bytes('0xfb'): 251,
sp.bytes('0xfc'): 252, sp.bytes('0xfd'): 253,
sp.bytes('0xfe'): 254, sp.bytes('0xff'): 255
}
)
@sp.entry_point
def hash_to_nat(self, hash_bytes):
sp.set_type(hash_bytes, sp.TBytes)
# Convert given string (as bytes) to sha256 hash
self.data.hash = sp.sha256(hash_bytes)
# Iterate over each byte of the hash and look up the corresponding int value from our big table above
x = sp.local('x', 0)
# Store the 'total' Nat value of the bytes
total = sp.local('total', 0)
hash_len = sp.local("hash_len", sp.len(self.data.hash))
sp.while x.value < hash_len.value:
# Add to previous total and then multiple the new value by 256
total.value = (total.value + self.data.bytes_to_nat[sp.slice(self.data.hash, x.value, 1).open_some()]) * 256
x.value = x.value + 1
# Save this here to allow the test to verify
self.data.nat_value = total.value
sp.trace(self.data.nat_value)
@sp.add_test(name = "sha256 to nat")
def test():
scenario = sp.test_scenario()
c1 = HashToNat()
scenario += c1
c1.hash_to_nat(sp.utils.bytes_of_string('abc'))
scenario.verify(c1.data.nat_value == 21591646332695244893830101741604419496986850225924227680630524372407505175031040)
c1.hash_to_nat(sp.utils.bytes_of_string(''))
scenario.verify(c1.data.nat_value == 26364758079885848839561014354580818634188774565938696958305125632285610262156544)