How do I convert to Crystal ruby's multiple assignments of Array

0 votes

I have a small (formerly) ruby blockchain script I'm trying to convert over into Crystal, that looks like this so far:

#  build your own blockchain from scratch in crystal!
#
#  to run use:
#    $ crystal ./blockchain_with_proof_of_work.cr

require "openssl" # for hash checksum digest function SHA256

class Block
  getter index : Int32
  getter timestamp : Time
  getter data : String
  getter previous_hash : String
  getter nonce : Int32 # # proof of work if hash starts with leading zeros (00)
  getter hash : String

  def initialize(index, data, previous_hash)
    @index = index
    @timestamp = Time.now
    @data = data
    @previous_hash = previous_hash
    @nonce, @hash = compute_hash_with_proof_of_work
  end

  def compute_hash_with_proof_of_work(difficulty = "00")
    nonce = 0
    loop do
      hash = calc_hash_with_nonce(nonce)
      if hash.starts_with?(difficulty)
        return [nonce, hash] # # bingo! proof of work if hash starts with leading zeros (00)
      else
        nonce += 1 # # keep trying (and trying and trying)
      end
    end
  end

  def calc_hash_with_nonce(nonce = 0)
    sha = OpenSSL::Digest.new("SHA256")
    sha.update(nonce.to_s + @index.to_s + @timestamp.to_s + @data + @previous_hash)
    sha.hexdigest
  end

  def self.first(data = "Genesis") # create genesis (big bang! first) block
    # # uses index zero (0) and arbitrary previous_hash ("0")
    Block.new(0, data, "0")
  end

  def self.next(previous, data = "Transaction Data...")
    Block.new(previous.index + 1, data, previous.hash)
  end
end # class Block

#####
# # let's get started
# #   build a blockchain a block at a time

b0 = Block.first("Genesis")
b1 = Block.next(b0, "Transaction Data...")
b2 = Block.next(b1, "Transaction Data......")
b3 = Block.next(b2, "More Transaction Data...")

blockchain = [b0, b1, b2, b3]

puts blockchain

######
#  will print something like:
#
# [#<Block:0x1e204f0
#   @data="Genesis",
#   @hash="00b8e77e27378f9aa0afbcea3a2882bb62f6663771dee053364beb1887e18bcf",
#   @index=0,
#   @nonce=242,
#   @previous_hash="0",
#   @timestamp=2017-09-20 20:13:38 +0200>,
#  #<Block:0x1e56e20
#   @data="Transaction Data...",
#   @hash="00aae8d2e9387e13c71b33f8cd205d336ac250d2828011f5970062912985a9af",
#   @index=1,
#   @nonce=46,
#   @previous_hash=
#    "00b8e77e27378f9aa0afbcea3a2882bb62f6663771dee053364beb1887e18bcf",
#   @timestamp=2017-09-20 20:13:38 +0200>,
#  #<Block:0x1e2bd58
#   @data="Transaction Data......",
#   @hash="00ea45e0f4683c3bec4364f349ee2b6816be0c9fd95cfd5ffcc6ed572c62f190",
#   @index=2,
#   @nonce=350,
#   @previous_hash=
#    "00aae8d2e9387e13c71b33f8cd205d336ac250d2828011f5970062912985a9af",
#   @timestamp=2017-09-20 20:13:38 +0200>,
#  #<Block:0x1fa8338
#   @data="More Transaction Data...",
#   @hash="00436f0fca677652963e904ce4c624606a255946b921132d5b1f70f7d86c4ab8",
#   @index=3,
#   @nonce=59,
#   @previous_hash=
#    "00ea45e0f4683c3bec4364f349ee2b6816be0c9fd95cfd5

However when I run it I get an error that states:

Error in blockchain.cr/blockchain_with_proof_of_work.cr:57: instantiating 
'Block:Class#first(String)'

b0 = Block.first("Genesis")
           ^~~~~

in blockchain.cr/blockchain_with_proof_of_work.cr:45: instantiating 
'Block:Class#new(Int32, String, String)'

Block.new(0, data, "0")
      ^~~

in blockchain.cr/blockchain_with_proof_of_work.cr:22: instance variable 
'@nonce' of Block must be Int32, not (Int32 | String)

    @nonce, @hash = compute_hash_with_proof_of_work
    ^~~~~~

Looking at Crystal docs on multiple assignment, I'm unsure of how I can refactor this method so that it doesn't fail Crystal's automatic static type checking and type inference? The method in question, of an array of two types being returned, doesn't seem covered by the docs:

@nonce, @hash = compute_hash_with_proof_of_work # return [nonce, hash]
Aug 10, 2018 in Blockchain by sabby
• 4,050 points
22 views

1 answer to this question.

Your answer

Your name to display (optional):
Privacy: Your email address will only be used for sending these notifications.
0 votes

When decomposing an Array into a multiple assignment Crystal can't infer the exact type of each element. So the value assigned to the instance variable @nonce could be either Int32 or String. You should use a Tuple instead: return {nonce, hash} (in line 29). A tuple has positional type declarations and is by the way more performant than an Array because it does not allocate memory on the heap.

answered Aug 10, 2018 by Perry
• 16,450 points

Related Questions In Blockchain

+1 vote
1 answer

How do i change the names of validating peers in IBM Bluemix blockchain?

IBM Bluemix Blockchain service Hyperledger Fabric v0.6 will ...READ MORE

answered Apr 10, 2018 in Blockchain by Perry
71 views
0 votes
1 answer
0 votes
1 answer

How do I use hyperledger fabric blockchain to build my own application?

To use fabric I would recommend you ...READ MORE

answered May 16, 2018 in Blockchain by Johnathon
• 9,070 points
51 views
0 votes
1 answer

Truffle tests not running after truffle init

This was a bug. They've fixed it. ...READ MORE

answered Sep 11, 2018 in Blockchain by Christine
• 15,790 points
88 views
+1 vote
3 answers

Removing double quotes from a string from JSON response in PHP

Just remove the json_encode call, and it should work: $resp ...READ MORE

answered Sep 12, 2018 in Blockchain by digger
• 27,620 points
1,823 views
0 votes
1 answer
0 votes
1 answer

How do I send back ethers to the sender of the tokens in a smart contract?

Whenever a smart contract receives ether via ...READ MORE

answered May 30, 2018 in Blockchain by Perry
• 16,450 points
217 views
0 votes
1 answer

© 2018 Brain4ce Education Solutions Pvt. Ltd. All rights Reserved.
"PMP®","PMI®", "PMI-ACP®" and "PMBOK®" are registered marks of the Project Management Institute, Inc. MongoDB®, Mongo and the leaf logo are the registered trademarks of MongoDB, Inc.