Class: IOTA::Multisig::Multisig
- Inherits:
-
Object
- Object
- IOTA::Multisig::Multisig
- Defined in:
- lib/iota/multisig/multisig.rb
Instance Method Summary collapse
- #addSignature(bundleToSign, inputAddress, key) ⇒ Object
- #getDigest(seed, index, security) ⇒ Object
- #getKey(seed, index, security) ⇒ Object
-
#initialize(client) ⇒ Multisig
constructor
A new instance of Multisig.
- #initiateTransfer(input, remainderAddress = nil, transfers) ⇒ Object
- #validateAddress(multisigAddress, digests) ⇒ Object
Constructor Details
Instance Method Details
#addSignature(bundleToSign, inputAddress, key) ⇒ Object
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 201 202 203 204 205 206 |
# File 'lib/iota/multisig/multisig.rb', line 137 def addSignature(bundleToSign, inputAddress, key) bundleToSign = [bundleToSign] if bundleToSign.class != Array bundle = IOTA::Crypto::Bundle.new(bundleToSign) # Get the security used for the private key # 1 security level = 2187 trytes security = (key.length / 2187).to_i # convert private key trytes into trits key = @converter.trits(key) # First get the total number of already signed transactions # use that for the bundle hash calculation as well as knowing # where to add the signature numSignedTxs = 0 (0...bundle.bundle.length).step(1) do |i| bundle.bundle[i] = IOTA::Models::Transaction.new(bundle.bundle[i]) if bundle.bundle[i].class != IOTA::Models::Transaction if bundle.bundle[i].address === inputAddress # If transaction is already signed, increase counter if !@validator.isAllNine(bundle.bundle[i].signatureMessageFragment) numSignedTxs += 1 else # sign the transactions bundleHash = bundle.bundle[i].bundle # First 6561 trits for the firstFragment firstFragment = key.slice(0, 6561) # Get the normalized bundle hash normalizedBundleHash = bundle.normalizedBundle(bundleHash) normalizedBundleFragments = [] # Split hash into 3 fragments (0...3).step(1) do |k| normalizedBundleFragments[k] = normalizedBundleHash.slice(k * 27, 27) end # First bundle fragment uses 27 trytes firstBundleFragment = normalizedBundleFragments[numSignedTxs % 3] # Calculate the new signatureFragment with the first bundle fragment firstSignedFragment = @signing.signatureFragment(firstBundleFragment, firstFragment) # Convert signature to trytes and assign the new signatureFragment bundle.bundle[i].signatureMessageFragment = @converter.trytes(firstSignedFragment) (1...security).step(1) do |j| # Next 6561 trits for the firstFragment nextFragment = key.slice(6561 * j, 6561) # Use the next 27 trytes nextBundleFragment = normalizedBundleFragments[(numSignedTxs + j) % 3] # Calculate the new signatureFragment with the first bundle fragment nextSignedFragment = @signing.signatureFragment(nextBundleFragment, nextFragment) # Convert signature to trytes and add new bundle entry at i + j position # Assign the signature fragment bundle.bundle[i + j].signatureMessageFragment = @converter.trytes(nextSignedFragment) end break end end end bundle.bundle end |
#getDigest(seed, index, security) ⇒ Object
17 18 19 20 |
# File 'lib/iota/multisig/multisig.rb', line 17 def getDigest(seed, index, security) pk = getPrivateKey(seed, index, security) @converter.trytes(pk.digests) end |
#getKey(seed, index, security) ⇒ Object
12 13 14 15 |
# File 'lib/iota/multisig/multisig.rb', line 12 def getKey(seed, index, security) pk = getPrivateKey(seed, index, security) @converter.trytes(pk.key) end |
#initiateTransfer(input, remainderAddress = nil, transfers) ⇒ Object
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 |
# File 'lib/iota/multisig/multisig.rb', line 39 def initiateTransfer(input, remainderAddress = nil, transfers) input = IOTA::Models::Input.new(input) if input.class != IOTA::Models::Input # If message or tag is not supplied, provide it # Also remove the checksum of the address if it's there (0...transfers.length).step(1) do |i| transfers[i] = IOTA::Models::Transfer.new(transfers[i]) if transfers[i].class != IOTA::Models::Transfer end # Input validation of transfers object raise StandardError, "Invalid transfers provided" if !@validator.isTransfersArray(transfers) # check if int raise StandardError, "Invalid inputs provided" if !@validator.isValue(input.security) # validate input address raise StandardError, "Invalid input address provided" if !@validator.isAddress(input.address) # validate remainder address raise StandardError, "Invalid remainder address provided" if remainderAddress && !@validator.isAddress(remainderAddress) remainderAddress = @utils.noChecksum(remainderAddress) if remainderAddress && remainderAddress.length == 90 # Create a new bundle bundle = IOTA::Crypto::Bundle.new totalValue = 0 signatureFragments = [] tag = nil # Iterate over all transfers, get totalValue and prepare the signatureFragments, message and tag (0...transfers.length).step(1) do |i| signatureMessageLength = 1 # If message longer than 2187 trytes, increase signatureMessageLength (add multiple transactions) if transfers[i]..length > 2187 # Get total length, message / maxLength (2187 trytes) signatureMessageLength += (transfers[i]..length / 2187).floor msgCopy = transfers[i]. # While there is still a message, copy it while msgCopy fragment = msgCopy.slice(0, 2187) msgCopy = msgCopy.slice(2187, msgCopy.length) # Pad remainder of fragment fragment += (['9']*(2187-fragment.length)).join('') if fragment.length < 2187 signatureFragments.push(fragment) end else # Else, get single fragment with 2187 of 9's trytes fragment = '' fragment = transfers[i]..slice(0, 2187) if transfers[i]. # Pad remainder of fragment fragment += (['9']*(2187-fragment.length)).join('') if fragment.length < 2187 signatureFragments.push(fragment) end # get current timestamp in seconds = Time.now.utc.to_i # If no tag defined, get 27 tryte tag. tag = transfers[i].obsoleteTag ? transfers[i].obsoleteTag : (['9']*27).join('') # Pad for required 27 tryte length tag += (['9']*(27-tag.length)).join('') if tag.length < 27 # Add first entries to the bundle # Slice the address in case the user provided a checksummed one bundle.addEntry(signatureMessageLength, transfers[i].address.slice(0, 81), transfers[i].value, tag, ) # Sum up total value totalValue += transfers[i].value.to_i end # Get inputs if we are sending tokens if totalValue > 0 if input.balance return createBundle(input.balance, totalValue, bundle, input, remainderAddress, tag, signatureFragments) else @api.getBalances([input.address], 100) do |st1, balances| if !st1 raise StandardError, "Error fetching balances: #{balances}" else return createBundle(balances[0].to_i, totalValue, bundle, input, remainderAddress, tag, signatureFragments) end end end else raise StandardError, "Invalid value transfer: the transfer does not require a signature." end end |
#validateAddress(multisigAddress, digests) ⇒ Object
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/iota/multisig/multisig.rb', line 22 def validateAddress(multisigAddress, digests) kerl = IOTA::Crypto::Kerl.new # Absorb all key digests digests.each do |digest| trits = @converter.trits(digest) kerl.absorb(@converter.trits(digest), 0, trits.length) end # Squeeze address trits addressTrits = [] kerl.squeeze(addressTrits, 0, IOTA::Crypto::Kerl::HASH_LENGTH) # Convert trits into trytes and return the address @converter.trytes(addressTrits) === multisigAddress end |