|
| 1 | +# coding=utf-8 |
| 2 | +""" |
| 3 | +Example of how to use PyOTA's multisig feature. |
| 4 | +
|
| 5 | +This script will generate a multisig address using private keys from |
| 6 | +three different seeds, prepare a bundle, sign the inputs, and then |
| 7 | +finally broadcast the transactions to the Tangle. |
| 8 | +
|
| 9 | +References: |
| 10 | + - https://github.com/iotaledger/wiki/blob/master/multisigs.md |
| 11 | +""" |
| 12 | + |
| 13 | +from __future__ import absolute_import, division, print_function, \ |
| 14 | + unicode_literals |
| 15 | + |
| 16 | +from typing import List |
| 17 | + |
| 18 | +from iota import Address, ProposedTransaction, Tag, TransactionTrytes, \ |
| 19 | + TryteString |
| 20 | +from iota.crypto.types import Digest, Seed |
| 21 | +from iota.multisig import MultisigIota |
| 22 | +from iota.multisig.types import MultisigAddress |
| 23 | + |
| 24 | +""" |
| 25 | +Step 1: Each participant generates one or more digests that will be |
| 26 | +used to create the multisig address. |
| 27 | +
|
| 28 | +For this example, we will create digests from 3 different private |
| 29 | +keys, each owned by a different seed. |
| 30 | +
|
| 31 | +Note that this part normally happens on separate computers, so that |
| 32 | +participants don't have to share their seeds. |
| 33 | +""" |
| 34 | + |
| 35 | +## |
| 36 | +# Create digest 1 of 3. |
| 37 | +# |
| 38 | +# noinspection SpellCheckingInspection |
| 39 | +api_1 =\ |
| 40 | + MultisigIota( |
| 41 | + adapter = 'http://localhost:14265', |
| 42 | + |
| 43 | + seed = |
| 44 | + Seed( |
| 45 | + b'TESTVALUE9DONTUSEINPRODUCTION99999XKMYQP' |
| 46 | + b'OIFGQSMIIWCQVMBSOKZASRQOFSIUSSHNDKVL9PJVS', |
| 47 | + ), |
| 48 | + ) |
| 49 | + |
| 50 | +gd_result =\ |
| 51 | + api_1.get_digests( |
| 52 | + # Starting key index. |
| 53 | + index = 0, |
| 54 | + |
| 55 | + # Number of digests to generate. |
| 56 | + count = 1, |
| 57 | + |
| 58 | + # Security level of the resulting digests. |
| 59 | + # Must be a value between 1 (faster) and 3 (more secure). |
| 60 | + security_level = 3, |
| 61 | + ) |
| 62 | + |
| 63 | +# ``get_digests`` returns a dict which contains 1 or more digests, |
| 64 | +# depending on what value we used for ``count``. |
| 65 | +digest_1 = gd_result['digests'][0] # type: Digest |
| 66 | + |
| 67 | +## |
| 68 | +# Create digest 2 of 3. |
| 69 | +# |
| 70 | +# noinspection SpellCheckingInspection |
| 71 | +api_2 =\ |
| 72 | + MultisigIota( |
| 73 | + adapter = 'http://localhost:14265', |
| 74 | + |
| 75 | + seed = |
| 76 | + Seed( |
| 77 | + b'TESTVALUE9DONTUSEINPRODUCTION99999DDWDKI' |
| 78 | + b'FFBZVQHHINYDWRSMGGPZUERNLEAYMLFPHRXEWRNST', |
| 79 | + ), |
| 80 | + ) |
| 81 | + |
| 82 | +# You can use any starting index that you want. |
| 83 | +# For maximum security, each index should be used only once. |
| 84 | +gd_result = api_2.get_digests(index=42, count=1, security_level=3) |
| 85 | + |
| 86 | +digest_2 = gd_result['digests'][0] # type: Digest |
| 87 | + |
| 88 | +## |
| 89 | +# Create digest 3 of 3. |
| 90 | +# |
| 91 | +# noinspection SpellCheckingInspection |
| 92 | +api_3 =\ |
| 93 | + MultisigIota( |
| 94 | + adapter = 'http://localhost:14265', |
| 95 | + |
| 96 | + seed = |
| 97 | + Seed( |
| 98 | + b'TESTVALUE9DONTUSEINPRODUCTION99999JYFRTI' |
| 99 | + b'WMKVVBAIEIYZDWLUVOYTZBKPKLLUMPDF9PPFLO9KT', |
| 100 | + ), |
| 101 | + ) |
| 102 | + |
| 103 | +# It is not necessary for every digest to have the same security level. |
| 104 | +gd_result = api_3.get_digests(index=8, count=1, security_level=2) |
| 105 | + |
| 106 | +digest_3 = gd_result['digests'][0] # type: Digest |
| 107 | + |
| 108 | + |
| 109 | +""" |
| 110 | +Step 2: Collect the digests and create a multisig address. |
| 111 | +
|
| 112 | +Note that digests are safe to share with other users, so this step is |
| 113 | +typically performed on a single instance. |
| 114 | +
|
| 115 | +IMPORTANT: Keep track of the order that digests are used; you will |
| 116 | +need to ensure that the same order is used to sign inputs! |
| 117 | +""" |
| 118 | + |
| 119 | +cma_result =\ |
| 120 | + api_1.create_multisig_address(digests=[digest_1, digest_2, digest_3]) |
| 121 | + |
| 122 | +# For consistency, every API command returns a dict, even if it only |
| 123 | +# has a single value. |
| 124 | +multisig_address = cma_result['address'] # type: MultisigAddress |
| 125 | + |
| 126 | + |
| 127 | +""" |
| 128 | +Step 3: Prepare the bundle. |
| 129 | +
|
| 130 | +This step occurs some time later, after the multisig address has |
| 131 | +received some IOTAs. |
| 132 | +
|
| 133 | +IMPORTANT: In IOTA, it is unsafe to spend from a single address |
| 134 | +multiple times. Take care to spend ALL of the IOTAs controlled by the |
| 135 | +multisig address, or generate a new multisig address that will receive |
| 136 | +the change from the transaction! |
| 137 | +""" |
| 138 | + |
| 139 | +# noinspection SpellCheckingInspection |
| 140 | +pmt_result =\ |
| 141 | + api_1.prepare_multisig_transfer( |
| 142 | + # These are the transactions that will spend the IOTAs. |
| 143 | + # You can divide up the IOTAs to send to multiple addresses if you |
| 144 | + # want, but to keep this example focused, we will only include a |
| 145 | + # single spend transaction. |
| 146 | + transfers = [ |
| 147 | + ProposedTransaction( |
| 148 | + address = |
| 149 | + Address( |
| 150 | + b'TESTVALUE9DONTUSEINPRODUCTION99999NDGYBC' |
| 151 | + b'QZJFGGWZ9GBQFKDOLWMVILARZRHJMSYFZETZTHTZR', |
| 152 | + ), |
| 153 | + |
| 154 | + value = 42, |
| 155 | + |
| 156 | + # If you'd like, you may include an optional tag and/or |
| 157 | + # message. |
| 158 | + tag = Tag(b'KITTEHS'), |
| 159 | + message = TryteString.from_string('thanx fur cheezburgers'), |
| 160 | + ), |
| 161 | + ], |
| 162 | + |
| 163 | + # Specify our multisig address as the input for the spend |
| 164 | + # transaction(s). |
| 165 | + # Note that PyOTA currently only allows one multisig input per |
| 166 | + # bundle (although the protocol does not impose a limit). |
| 167 | + multisig_input = multisig_address, |
| 168 | + |
| 169 | + # If there will be change from this transaction, you MUST specify |
| 170 | + # the change address! Unlike regular transfers, multisig transfers |
| 171 | + # will NOT automatically generate a change address; that wouldn't |
| 172 | + # be fair to the other participants! |
| 173 | + change_address = None, |
| 174 | + ) |
| 175 | + |
| 176 | +prepared_trytes = pmt_result['trytes'] # type: List[TransactionTrytes] |
| 177 | + |
| 178 | + |
| 179 | +""" |
| 180 | +Step 4: Sign the inputs. |
| 181 | +
|
| 182 | +Note that we must apply signatures in the same order as when we created |
| 183 | +the multisig address! |
| 184 | +""" |
| 185 | +# :todo: |
| 186 | +signed_trytes = None # type: List[TransactionTrytes] |
| 187 | + |
| 188 | + |
| 189 | +""" |
| 190 | +Step 5: Broadcast the bundle. |
| 191 | +
|
| 192 | +Note that this step works the same as any other transfer. |
| 193 | +""" |
| 194 | +api_1.send_trytes(trytes=signed_trytes, depth=3) |
0 commit comments