Multi-signature account

Multi-signature accounts can be used to require that transactions require multiple public keys to sign before they are considered valid. This is done by first configuring your account’s threshold levels. Each operation has a threshold level of either low, medium, or high. You give each threshold level a number between 1-255 in your account. Then, for each key in your account, you assign it a weight (1-255, setting a 0 weight deletes the key). Any transaction must be signed with enough keys to meet the threshold.

For example, let’s say you set your threshold levels; low = 1, medium = 2, high = 3. You want to send a payment operation, which is a medium threshold operation. Your master key has weight 1. Additionally, you have a secondary key associated with your account which has a weight of 1. Now, the transaction you submit for this payment must include both signatures of your master key and secondary key since their combined weight is 2 which is enough to authorize the payment operation.

In this example, we will:

  • Add a second signer to the account

  • Set our account’s masterkey weight and threshold levels

  • Create a multi signature transaction that sends a payment

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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
"""
Stellar uses signatures as authorization. Transactions always need authorization
from at least one public key in order to be considered valid. Generally,
transactions only need authorization from the public key of the source account.

Transaction signatures are created by cryptographically signing the
transaction object contents with a secret key. Stellar currently uses the ed25519 signature
scheme, but there’s also a mechanism for adding additional types of public/private key schemes.
A transaction with an attached signature is considered to have authorization from that public key.

In two cases, a transaction may need more than one signature. If the transaction has
operations that affect more than one account, it will need authorization from every account
in question. A transaction will also need additional signatures if the account associated
with the transaction has multiple public keys.

See: https://developers.stellar.org/docs/glossary/multisig/
"""
from stellar_sdk import Server, TransactionBuilder, Signer, Network, Keypair

server = Server(horizon_url="https://horizon-testnet.stellar.org")
root_keypair = Keypair.from_secret(
    "SA6XHAH4GNLRWWWF6TEVEWNS44CBNFAJWHWOPZCVZOUXSQA7BOYN7XHC"
)
root_account = server.load_account(account_id=root_keypair.public_key)
secondary_keypair = Keypair.from_secret(
    "SAMZUAAPLRUH62HH3XE7NVD6ZSMTWPWGM6DS4X47HLVRHEBKP4U2H5E7"
)

secondary_signer = Signer.ed25519_public_key(
    account_id=secondary_keypair.public_key, weight=1
)
transaction = (
    TransactionBuilder(
        source_account=root_account,
        network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE,
        base_fee=100,
    )
    .append_set_options_op(
        master_weight=1,  # set master key weight
        low_threshold=1,
        med_threshold=2,  # a payment is medium threshold
        high_threshold=2,  # make sure to have enough weight to add up to the high threshold!
        signer=secondary_signer,
    )
    .set_timeout(30)
    .build()
)

# only need to sign with the root signer as the 2nd signer won't
# be added to the account till after this transaction completes
transaction.sign(root_keypair)
response = server.submit_transaction(transaction)
print(response)

# now create a payment with the account that has two signers
destination = "GBA5SMM5OYAOOPL6R773MV7O3CCLUDVLCWHIVVL3W4XTD3DA5FJ4JSEZ"
transaction = (
    TransactionBuilder(
        source_account=root_account,
        network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE,
        base_fee=100,
    )
    .append_payment_op(destination=destination, amount="2000", asset_code="XLM")
    .set_timeout(30)
    .build()
)

# now we need to sign the transaction with both the root and the secondary_keypair
transaction.sign(root_keypair)
transaction.sign(secondary_keypair)
response = server.submit_transaction(transaction)
print(response)