Multi-signature¶
Background¶
Multiple signature functions allow for permission grading, and each permission can correspond to multiple private keys. This makes it possible to achieve multi-person joint control of accounts. This guide walks the user through TRON's multi-signature implementation and design.
Reference: TIP-16: Account Multi-signature
Concept¶
There are three types of permission: ownerăwitness and active. Owner permission has the right to execute all the contracts. Witness permission is for SR. Active permission contains a set of contracts selected execution permissions.
Protocol Definition¶
Account¶
message Account {
// ...
Permission owner_permission = 31;
Permission witness_permission = 32;
repeated Permission active_permission = 33;
}
Three attributes are added, owner_permissionăwitness_permission and active_permission. active_permission is a list, the length can not be bigger than 8.
ContractType¶
message Transaction {
message Contract {
enum ContractType {
AccountCreateContract = 0;
// ...
AccountPermissionUpdateContract = 46;
}
}
}
AccountPermissionUpdateContract is a new contract type used to update the account permission.
AccountPermissionUpdateContract¶
message AccountPermissionUpdateContract {
bytes owner_address = 1;
Permission owner = 2;
Permission witness = 3;
repeated Permission actives = 4;
}
owner_address
: The account applies multi-signaturesowner
: Owner permissionwitness
: Witness permission (if is witness)actives
: Active permission
This will override the Original account permission. Therefore, if you only want to modify the owner permission, witness (if it is a witnss account) and active permission also need to be set
Permission¶
message Permission {
enum PermissionType {
Owner = 0;
Witness = 1;
Active = 2;
}
PermissionType type = 1;
int32 id = 2;
string permission_name = 3;
int64 threshold = 4;
int32 parent_id = 5;
bytes operations = 6;
repeated Key keys = 7;
}
PermissionType
: Permission typeid
: Generated by system. Owner id=0, Witness id=1, Active id increases from 2. Specifying using which permission to execute a contract by setting id. For instance, using owner permission, set id=0permission_name
: Permission name, 32 bytes length limitthreshold
: The threshold of the signature weightparent_id
: Current 0-
operations
: 32 bytes (256 b), each bit represent the execution permission of one contract, 1 means it owns the execution permission of the contract.For instance, operations=0x0100...00(hex), 100...0(binary), refer to the definition of Transaction.ContractType in proto, the id of AccountCreateContract is 0, means this permission only owns the execution permission of AccountCreateContract
-
keys
: The accounts and weights that all own the permission, 5 keys at most.
Key¶
message Key {
bytes address = 1;
int64 weight = 2;
}
address
: The account addressweight
: The signature weight
Transaction¶
message Transaction {
// ...
int32 Permission_id = 5;
}
Permission_id
is added. It is corresponding to Permission.id
1 is not allowed, because witness permission is only used to produce blocks, not for transaction signature.
Owner Permission¶
Owner permission is the top permission of an account. It is used to control account ownership, adjust permission structure. Owner Permission has the right to execute all the contracts.
Owner permission's features:
- The account that has owner permission can change the owner permission
- When owner permission is null, the default owner of the account owns the owner permission
- When you create a new account, the address will be insert into owner permission automatically, default weight is 1, keys field only contains this address and also weight is 1.
- If a permissionId is not specified when a contract is executed, using owner permission by default.
Witness Permission¶
Super representatives can use this permission to manage block producing. Only witness account has this permission.
Usage scenario example: A super representative deploys a witness node on cloud server. In order to keep the account on the cloud server safe, you can only give the block producing permission to the account you put on cloud server. Because this account only owns block producing permission, no TRX transfer permission, so even if the account on the cloud server is leaked, the TRX will not be lost.
Witness node configuration:
- if no witness permission is used, no need to configure
- if itness permission is used, need to reconfigure:
# config.conf
// Optional.The default is empty.
// It is used when the witness account has set the witnessPermission.
// When it is not empty, the localWitnessAccountAddress represents the address of the witness account,
// and the localwitness is configured with the private key of the witnessPermissionAddress in the witness account.
// When it is empty,the localwitness is configured with the private key of the witness account.
//localWitnessAccountAddress =
localwitness = [
f4df789d3210ac881cb900464dd30409453044d2777060a0c391cbdf4c6a4f57
]
Active Permission¶
Active permission is composed of a set of contract execution permission, like creating an account, transfer function, etc.
Active permission's features:
- the account owns owner permission can change active permission
- the account owns the execution permission of AccountPermissionUpdateContract can also change active permission
- 8 permissions at most
- permissionId increases from 2 automatically
- when a new account is created, an active permission will be created automatically, and the address will be inserted into it, default weight is 1, keys field only contains this address and weight is 1
Fee¶
- Using AccountPermissionUpdateContract costs 100TRX
- If a transaction contains 2 or more than 2 signatures, it charges an extra 1 TRX besides the transaction fee
- The fee can be modified by proposing
API¶
Change Permission¶
AccountPermissionUpdateContract
, steps:
- call
getaccount
to query the account, get the original permission - change permission
- build transaction and sign
- send transaction
Demo HTTP request:
// POST to http://{{host}}:{{port}}/wallet/accountpermissionupdate
{
"owner_address": "41ffa9466d5bf6bb6b7e4ab6ef2b1cb9f1f41f9700",
"owner": {
"type": 0,
"id": 0,
"permission_name": "owner",
"threshold": 2,
"keys": [{
"address": "41F08012B4881C320EB40B80F1228731898824E09D",
"weight": 1
},
{
"address": "41DF309FEF25B311E7895562BD9E11AAB2A58816D2",
"weight": 1
},
{
"address": "41BB7322198D273E39B940A5A4C955CB7199A0CDEE",
"weight": 1
}
]
},
"witness": {
"type": 1,
"id": 1,
"permission_name": "witness",
"threshold": 1,
"keys": [{
"address": "41F08012B4881C320EB40B80F1228731898824E09D",
"weight": 1
}
]
},
"actives": [{
"type": 2,
"id": 2,
"permission_name": "active0",
"threshold": 3,
"operations": "7fff1fc0037e0000000000000000000000000000000000000000000000000000",
"keys": [{
"address": "41F08012B4881C320EB40B80F1228731898824E09D",
"weight": 1
},
{
"address": "41DF309FEF25B311E7895562BD9E11AAB2A58816D2",
"weight": 1
},
{
"address": "41BB7322198D273E39B940A5A4C955CB7199A0CDEE",
"weight": 1
}
]
}]
}
Calculate the Active Permission's Operations¶
public static void main(String[] args) {
//you need to specify the id of the contract you need to give permission to by referring to the definition of Transaction.ContractType in proto to get the id of the contract, below includes all the contract except AccountPermissionUpdateContract(id=46)
Integer[] contractId = {0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 30, 31,
32, 33, 41, 42, 43, 44, 45};
List<Integer> list = new ArrayList<>(Arrays.asList(contractId));
byte[] operations = new byte[32];
list.forEach(e -> {
operations[e / 8] |= (1 << e % 8);
});
//7fff1fc0033e0000000000000000000000000000000000000000000000000000
System.out.println(ByteArray.toHexString(operations));
}
Contract Execution¶
(1). Create transaction, the same as none multi-signatures
(2). Specify Permission_id
, default 0, represent owner permission, demo
(3). User A sign the transaction, and then send it to user B
(4). User B sign the transaction gets from A, and then send it to user C
......
(n). The last users that signs the transaction broadcast it to the node
(n+1). The node will verify if the sum of the weight of all signatures is bigger than threshold, if true, the transaction is accepted, otherwise, is rejected
Demo: MultiSignDemo.java
Other APIs¶
Please refer to HTTP API and RPC API for more information.
-
query the addresses that already signed a transaction
> curl -X POST http://127.0.0.1:8090/wallet/getapprovedlist -d '{"transaction"}'
rpc GetTransactionApprovedList(Transaction) returns (TransactionApprovedList) { }
-
query the signature weight of a transaction
> curl -X POST http://127.0.0.1:8090/wallet/getsignweight -d '{"transaction"}'
rpc GetTransactionSignWeight (Transaction) returns (TransactionSignWeight) {}