ADR-28 derive address functions (#9088)

* update Derive functions

* update adr-028

* changelog update

* Apply suggestions from code review

Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com>

* review updates

* remove DeriveMulti and rollback some changes in CHANGELOG

* add noop error check

Co-authored-by: Marie Gauthier <marie.gauthier63@gmail.com>
This commit is contained in:
Robert Zaremba
2021-04-15 21:32:45 +00:00
committed by GitHub
co-authored by Marie Gauthier
parent 261c7ebd89
commit e43edc4749
4 changed files with 45 additions and 21 deletions
@@ -135,7 +135,7 @@ type Addressable interface {
Address() []byte
}
func NewComposed(typ string, subaccounts []Addressable) []byte {
func Composed(typ string, subaccounts []Addressable) []byte {
addresses = map(subaccounts, \a -> LengthPrefix(a.Address()))
addresses = sort(addresses)
return address.Hash(typ, addresses[0] + ... + addresses[n])
@@ -175,7 +175,7 @@ func (multisig PubKey) Address() {
prefix := fmt.Sprintf("%s/%d", proto.MessageName(multisig), multisig.Threshold)
// use the Composed function defined above
return address.NewComposed(prefix, keys)
return address.Composed(prefix, keys)
}
```
@@ -185,14 +185,14 @@ NOTE: this section is not finalize and it's in active discussion.
In Basic Address section we defined a module account address as:
```
```go
address.Hash("module", moduleName)
```
We use `"module"` as a schema type for all module derived addresses. Module accounts can have sub accounts. The derivation process has a defined order: module name, submodule key, subsubmodule key.
Module account addresses are heavily used in the SDK so it makes sense to optimize the derivation process: instead of using of using `LengthPrefix` for the module name, we use a null byte (`'\x00'`) as a separator. This works, because null byte is not a part of a valid module name.
```
```go
func Module(moduleName string, key []byte) []byte{
return Hash("module", []byte(moduleName) + 0 + key)
}
@@ -208,24 +208,24 @@ If we want to create an address for a module account depending on more than one
btcAtomAMM := address.Module("amm", btc.Addrress() + atom.Address()})
```
We can continue the derivation process and can create an address for a submodule account.
#### Derived Addresses
```
func Submodule(address []byte, derivationKey []byte) {
return Hash("module", address + derivationKey)
We must be able to cryptographically derive one address from another one. The derivation process must guarantee hash properties, hence we use the already defined `Hash` function:
```go
func Derive(address []byte, derivationKey []byte) []byte {
return Hash(addres, derivationKey)
}
```
NOTE: if `address` is not a hash based address (with `LEN` length) then we should use `LengthPrefix`. An alternative would be to use one `Module` function, which takes a slice of keys and mapped with `LengthPrefix`. For final version we need to validate what's the most common use.
Note: `Module` is a special case of the more general _derived_ address, where we set the `"module"` string for the _from address_.
**Example** For a cosmwasm smart-contract address we could use the following construction:
```
smartContractAddr := Submodule(Module("cosmwasm", smartContractsNamespace), smartContractKey)
smartContractAddr := Derived(Module("cosmwasm", smartContractsNamespace), []{smartContractKey})
```
### Schema Types
A `typ` parameter used in `Hash` function SHOULD be unique for each account type.