github.com/cosmos/cosmos-sdk@v0.50.10/types/address/hash.go (about)

     1  package address
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/sha256"
     6  	"fmt"
     7  	"sort"
     8  
     9  	"github.com/cometbft/cometbft/crypto"
    10  
    11  	"cosmossdk.io/errors"
    12  
    13  	"github.com/cosmos/cosmos-sdk/internal/conv"
    14  )
    15  
    16  // Len is the length of base addresses
    17  const Len = sha256.Size
    18  
    19  // Addressable represents any type from which we can derive an address.
    20  type Addressable interface {
    21  	Address() []byte
    22  }
    23  
    24  // Hash creates a new address from address type and key.
    25  // The functions should only be used by new types defining their own address function
    26  // (eg public keys).
    27  func Hash(typ string, key []byte) []byte {
    28  	hasher := sha256.New()
    29  	_, err := hasher.Write(conv.UnsafeStrToBytes(typ))
    30  	// the error always nil, it's here only to satisfy the io.Writer interface
    31  	errors.AssertNil(err)
    32  	th := hasher.Sum(nil)
    33  
    34  	hasher.Reset()
    35  	_, err = hasher.Write(th)
    36  	errors.AssertNil(err)
    37  	_, err = hasher.Write(key)
    38  	errors.AssertNil(err)
    39  	return hasher.Sum(nil)
    40  }
    41  
    42  // Compose creates a new address based on sub addresses.
    43  func Compose(typ string, subAddresses []Addressable) ([]byte, error) {
    44  	as := make([][]byte, len(subAddresses))
    45  	totalLen := 0
    46  	var err error
    47  	for i := range subAddresses {
    48  		a := subAddresses[i].Address()
    49  		as[i], err = LengthPrefix(a)
    50  		if err != nil {
    51  			return nil, fmt.Errorf("not compatible sub-adddress=%v at index=%d [%w]", a, i, err)
    52  		}
    53  		totalLen += len(as[i])
    54  	}
    55  
    56  	sort.Slice(as, func(i, j int) bool { return bytes.Compare(as[i], as[j]) <= 0 })
    57  	key := make([]byte, totalLen)
    58  	offset := 0
    59  	for i := range as {
    60  		copy(key[offset:], as[i])
    61  		offset += len(as[i])
    62  	}
    63  	return Hash(typ, key), nil
    64  }
    65  
    66  // Module is a specialized version of a composed address for modules. Each module account
    67  // is constructed from a module name and a sequence of derivation keys (at least one
    68  // derivation key must be provided). The derivation keys must be unique
    69  // in the module scope, and is usually constructed from some object id. Example, let's
    70  // a x/dao module, and a new DAO object, it's address would be:
    71  //
    72  //	address.Module(dao.ModuleName, newDAO.ID)
    73  func Module(moduleName string, derivationKeys ...[]byte) []byte {
    74  	mKey := []byte(moduleName)
    75  	if len(derivationKeys) == 0 { // fallback to the "traditional" ModuleAddress
    76  		return crypto.AddressHash(mKey)
    77  	}
    78  	// need to append zero byte to avoid potential clash between the module name and the first
    79  	// derivation key
    80  	mKey = append(mKey, 0)
    81  	addr := Hash("module", append(mKey, derivationKeys[0]...))
    82  	for _, k := range derivationKeys[1:] {
    83  		addr = Derive(addr, k)
    84  	}
    85  	return addr
    86  }
    87  
    88  // Derive derives a new address from the main `address` and a derivation `key`.
    89  // This function is used to create a sub accounts. To create a module accounts use the
    90  // `Module` function.
    91  func Derive(address, key []byte) []byte {
    92  	return Hash(conv.UnsafeBytesToStr(address), key)
    93  }