Multi-signature

Background

Note

Since v3.5 In the past version, the transactions created in one account can only be signed by one private key, an account can only be managed by one private key. Since V3.5, an account can be managed by several private keys, and the transactions created in one account can be signed by several private keys.

Concept

There are three types of permission: owner, executive and active. Owner permission has the right to execute all the contracts. Executive permission is for Governor. Active permission contains a set of contracts selected execution permissions.

Protocol Definition

Account

message Account {
  // ...
  Permission owner_permission = 31;
  Permission executive_permission = 32;
  repeated Permission active_permission = 33;
}

Three attributes are added, owner_permission, executive_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 executive = 3;
  repeated Permission actives = 4;
}
  • owner_address: The account applies multi-signatures
  • owner: Owner permission
  • executive: Executive permission (if is executive)
  • actives: Active permission

This will override the Original account permission.

Permission

message Permission {
  enum PermissionType {
    Owner = 0;
    Executive = 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 type
  • id: Generated by system. Owner id=0, Executive 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=0
  • permission_name: Permission name, 32 bytes length limit
  • threshold: The threshold of the signature weight
  • parent_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 address
  • weight: 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 executive 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:

  1. The account that has owner permission can change the owner permission
  2. When owner permission is null, the default owner of the account owns the owner permission
  3. 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.
  4. If a permissionId is not specified when a contract is executed, using owner permission by defualt.

Executive Permission

Governors can use this permission to manage block producing. Only executive account has this permission.

Usage scenario example: A governor deploys a executive 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 STB transfer permission, so even if the account on the cloud server is leaked, the STB will not be lost.

Executive node configuration:

  1. if no executive permission is used, no need to configure
  2. if itness permission is used, need to reconfigure:
# config.conf

// Optional.The default is empty.
// It is used when the executive account has set the executivePermission.
// When it is not empty, the localExecutiveAccountAddress represents the address of the executive account,
// and the localexecutive is configured with the private key of the executivePermissionAddress in the executive account.
// When it is empty,the localexecutive is configured with the private key of the executive account.

//localExecutiveAccountAddress =

localexecutive = [
  f4df789d3210ac881cb900464dd30409453044d2777060a0c391cbdf4c6a4f57
]

Active Permission

Active permission is composed of a set of contract execution permission, like creating an account, trnasfer function, etc.

Active permission's features:

  1. the account owns owner permission can change active permission
  2. the account owns the execution permission of AccountPermissionUpdateContract can also change active permission
  3. 8 permissions at most
  4. permissionId increases from 2 automatically
  5. 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

  1. Using AccountPermissionUpdateContract costs 100STB
  2. If a transaction contains 2 or more than 2 signatures, it charges an extra 1 STB besides the transaction fee
  3. The fee can be modified by proposing

API

Change Permission

AccountPermissionUpdateContract, steps:

  1. call getaccount to query the account, get the original permission
  2. change permission
  3. build transaction and sign
  4. 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
      }
    ]
  },
  "executive": {
      "type": 1,
      "id": 1,
      "permission_name": "executive",
      "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

(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

Other APIs

Please refer to HTTP API and RPC API for more information.

  1. add signature

    ```console

    curl -X POST http://127.0.0.1:8090/wallet/addtransactionsign -d ' {"transaction": "TransferContract", "privateKey": "permissionkey1"}' ```

    protobuf rpc AddSign (TransactionSign) returns (TransactionExtention) {}

  2. query the addresses that already signed a transaction

    ```console

    curl -X POST http://127.0.0.1:8090/wallet/getapprovedlist -d '{"transaction"}' ```

    protobuf rpc GetTransactionApprovedList(Transaction) returns (TransactionApprovedList) { }

  3. query the signature weight of a transaction

    ```console

    curl -X POST http://127.0.0.1:8090/wallet/getsignweight -d '{"transaction"}' ```

    protobuf rpc GetTransactionSignWeight (Transaction) returns (TransactionSignWeight) {}

Others

Since V3.5, what is the change after a new account is created?

When to create a new account, an owner permission and active permission will be generated automatically. Owner permission only contains one key, the weight and threshold are both 1. Active permission also contains one key, the weight and threshold are both 1, and operations is "7fff1fc0033e0000000000000000000000000000000000000000000000000000", means it support the execution of all contracts except AccountPermissionUpdateContract. After V3.5, if there is a new system contract, the default operations value of the newly created account will change. The operations of existing accounts will not change.

Please refer to wallet-cli to check the usage of multi-signature.

Fees

If you update your account permission, the fee is 100 STB.

If a transaction is signed by more than 1 account, the fee is 1 STB.