package keeper import ( "context" "encoding/binary" "fmt" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/address" "github.com/CosmWasm/wasmd/x/wasm/types" ) // AddressGenerator abstract address generator to be used for a single contract address type AddressGenerator func(ctx context.Context, codeID uint64, checksum []byte) sdk.AccAddress // ClassicAddressGenerator generates a contract address using codeID and instanceID sequence func (k Keeper) ClassicAddressGenerator() AddressGenerator { return func(ctx context.Context, codeID uint64, _ []byte) sdk.AccAddress { instanceID := k.mustAutoIncrementID(ctx, types.KeySequenceInstanceID) return BuildContractAddressClassic(codeID, instanceID) } } // PredictableAddressGenerator generates a predictable contract address func PredictableAddressGenerator(creator sdk.AccAddress, salt, msg []byte, fixMsg bool) AddressGenerator { return func(_ context.Context, _ uint64, checksum []byte) sdk.AccAddress { if !fixMsg { // clear msg to not be included in the address generation msg = []byte{} } return BuildContractAddressPredictable(checksum, creator, salt, msg) } } // BuildContractAddressClassic builds an address for a contract. func BuildContractAddressClassic(codeID, instanceID uint64) sdk.AccAddress { contractID := make([]byte, 16) binary.BigEndian.PutUint64(contractID[:8], codeID) binary.BigEndian.PutUint64(contractID[8:], instanceID) return address.Module(types.ModuleName, contractID)[:types.ContractAddrLen] } // BuildContractAddressPredictable generates a contract address for the wasm module with len = types.ContractAddrLen using the // Cosmos SDK address.Module function. // Internally a key is built containing: // (len(checksum) | checksum | len(sender_address) | sender_address | len(salt) | salt| len(initMsg) | initMsg). // // All method parameter values must be valid and not nil. func BuildContractAddressPredictable(checksum []byte, creator sdk.AccAddress, salt, initMsg types.RawContractMessage) sdk.AccAddress { if len(checksum) != 32 { panic("invalid checksum") } if err := sdk.VerifyAddressFormat(creator); err != nil { panic(fmt.Sprintf("creator: %s", err)) } if err := types.ValidateSalt(salt); err != nil { panic(fmt.Sprintf("salt: %s", err)) } if err := initMsg.ValidateBasic(); len(initMsg) != 0 && err != nil { panic(fmt.Sprintf("initMsg: %s", err)) } checksum = UInt64LengthPrefix(checksum) creator = UInt64LengthPrefix(creator) salt = UInt64LengthPrefix(salt) initMsg = UInt64LengthPrefix(initMsg) key := make([]byte, len(checksum)+len(creator)+len(salt)+len(initMsg)) copy(key[0:], checksum) copy(key[len(checksum):], creator) copy(key[len(checksum)+len(creator):], salt) copy(key[len(checksum)+len(creator)+len(salt):], initMsg) return address.Module(types.ModuleName, key)[:types.ContractAddrLen] } // UInt64LengthPrefix prepend big endian encoded byte length func UInt64LengthPrefix(bz []byte) []byte { return append(sdk.Uint64ToBigEndian(uint64(len(bz))), bz...) }