- Published on
Cosmos Ecosystem의 Address
- Authors
- Name
- 이민기
- Github
- @mingi3442
Why Can’t an ACC Address Be Converted to a Valcons Address

Intro
Cosmos Ecosystem은 블록체인 간의 상호 운용성을 제공하는 분산형 네트워크입니다. 이 생태계에서는 각각 다른 목적을 가진 여러 유형의 주소들이 존재하며, 이러한 주소들은 모두 Bech32 형식으로 인코딩되어 사용됩니다.
최근 Cosmos 기반 프로젝트에서 검증인 노드의 모니터링 시스템을 구축하던 중, Account Address로부터 Consensus Address를 얻어야 하는 상황이 있었습니다. 하지만 단순한 주소 변환만으로는 원하는 결과를 얻을 수 없었고, 이는 Cosmos의 주소 체계에 대한 더 깊은 이해가 필요하다는 것을 깨닫게 해주었습니다.
이 글에서는 제가 겪은 문제를 통해 Cosmos 생태계의 주소 체계와 각 주소 유형들이 어떻게 서로 연관되어 있는지 자세히 알아보도록 하겠습니다.
1. Bech32 Address
Bech32는 코스모스 생태계를 포함한 여러 블록체인에서 사용되는 주소 인코딩 형식으로, 가독성이 높고, 오류 감지 기능이 포함된 구조를 가지고 있습니다.
Bech32는 아래와 같은 특징을 가집니다.
- 가독성: 주소가 모두 소문자로 구성되며, 일부 문자(O, I, L 등)는 사용되지 않음 → 사람이 쉽게 읽고 입력 가능
- 오류 감지 기능: 특정 오타를 자동 감지하여 잘못된 주소 입력을 방지
- 가변 길이 지원: 서로 다른 체인의 주소 형식에서도 일관성을 유지할 수 있음
1-1 Bech32 Address 구조
Bech32 주소는 프리픽스(Prefix), 구분자(“1”), 데이터 필드로 구성됩니다.
<prefix>1<bech32_encoded_data>
- Prefix:
cosmos
,osmosis
등 각 체인 별로 다르게 사용 - Separator: 비트 단위 인코딩에서 혼동을 줄이기 위해 prefix와 data사이에
1
을 사용 - Bech32 Encoded Data: 공개 키로부터 변환된 바이트 배열 데이터를 Bech32 방식으로 인코딩한 값
2. Account Address
Account Address는 일반 사용자가 지갑을 통해 사용하는 주소로, 토큰 전송 등에 사용됩니다.
cosmos1...
3. Validator Operator Address
Validator Operator Address는 검증인을 운영하기 위한 주소로, 블록 리워드 등을 받을 지갑 주소입니다.
cosmosvaloper1...
4. Validator Consensus Address
Validator Consensus Address는 검증인이 블록 검증을 위해 서명 등을 위한 노드의 주소입니다.
cosmosvalcons1...
5. Account Address를 통해 Validator Operator Address 변환 ✅
Cosmos SDK에서는 Bech32 Address를 바이트코드로 변환하는 함수와 바이트코드를 각 Account, Validator Operator, Validator Consensus Address로 변환하는 함수가 있습니다.
아래는 Account Address를 통해 Validator Operator Address로 변환하는 코드이며, 잘 작동합니다.
import(
sdk "github.com/cosmos/cosmos-sdk/types"
"fmt"
)
func ConvertToValidatorAddress(address string) (string, error) {
// Account Address(cosmos1...) -> byte[]로 변환
accAddr, err := sdk.AccAddressFromBech32(address)
if err != nil {
return "", fmt.Errorf("failed to convert address %s: %w", address, err)
}
// Account Address []bytes -> Validator Operator Address []bytes로 변환
valAddr := sdk.ValAddress(accAddr)
// Validator Operator Address []bytes -> Bech32 형식으로 변환
bech32ValAddr, err := sdk.Bech32ifyAddressBytes("cosmosvaloper", valAddr)
if err != nil {
return "", fmt.Errorf("failed to convert address %s: %w", address, err)
}
return bech32ValAddr, nil
}
6. Account Address를 통해 Validator Consensus Address 변환 ❌
마찬가지로 Cosmos SDK를 이용하여 Account Address를 통해 Validator Consensus Address로 변환하는 코드이며, 이 코드는 정상적으로 작동은 하지만 원하는 주소값이 나오지 않습니다.
import(
sdk "github.com/cosmos/cosmos-sdk/types"
"fmt"
)
func ConvertToConsensusAddress(address string) (string, error) {
// Account Address(cosmos1...) -> byte[]로 변환
accAddr, err := sdk.AccAddressFromBech32(address)
if err != nil {
return "", fmt.Errorf("failed to convert address %s: %w", address, err)
}
// Account Address []bytes -> Validator Consensus Address []bytes로 변환
consAddr := sdk.ConsAddress(accAddr)
// Validator Consensus Address []bytes -> Bech32 형식으로 변환
bech32ConsAddr, err := sdk.Bech32ifyAddressBytes("cosmosvalcons", consAddr)
if err != nil {
return "", fmt.Errorf("failed to convert address %s: %w", address, err)
}
return bech32ConsAddr, nil
}
7. 실제 Cosmos Key 구조
왜 같은 로직을 사용하더라도 Validator Consensus Address는 다르게 나올까요?

이유는 Validator Consensus Address가 Consensus Layer와 Application Layer에서 각각 사용하는 Public Key가 다르기 때문입니다.
위 그림은 Cosmos 생태계에서 사용되는 키와 주소의 관계를 보여줍니다. 크게 두 가지 경로로 키와 주소가 생성됩니다:
Application Layer
- Mnemonic으로부터 Private Key 생성
- Private Key로부터 Application Layer의 Bech32 Address bytes 생성
- 이 bytes로부터 Account Address, Validator Operator Address, Consensus Address를 생성
- 하지만 여기서 생성된 Consensus Address는 실제 사용되지 않음 (Use: X)
Consensus Layer
- Node 초기화 시 새로운 Private Key 생성
- Private Key로부터 Consensus Layer의 Bech32 Address bytes 생성
- 이 bytes로부터 실제 사용될 Consensus Address가 생성됨
- 여기서 생성되는 Account Address와 Validator Operator Address는 사용되지 않음 (Use: X)
두 Layer는 Create Validator
트랜잭션을 통해 서로 연결됩니다. 이때 Application Layer의 Validator Operator Address와 Consensus Layer의 Consensus Address가 매핑되어, 블록 생성 및 검증은 Consensus Layer의 키로, 리워드 수신은 Application Layer의 주소로 처리됩니다.
그러므로 제가 원하는 것은 Consensus Layer의 Validator Consensus Address인데, 앞서 본 코드는 Application Layer의 Bech32 Address를 Validator Consensus Address로 변환하였기 때문에 제가 원하는 주소가 나오지 않았습니다..
8. 다시 Account Address를 통해 Validator Consensus Address 변환 ✅
그러면 Consensus Layer의 Bech32 Address는 어떻게 알 수 있을까요?
답은 Staking 모듈에서 Validator를 통해, 해당 Validator Operator Address에 대한 Consensus Layer의 Public Key를 얻을 수 있습니다. 이를 통해 Bech32 Address를 구한 뒤 Validator Consensus Address를 구할 수 있었습니다.
func ConvertToConsensusAddress(accAddr string) (string, error) {
validatorAddr, err := ConvertToValidatorAddress(accAddr)
if err != nil {
return "",fmt.Errorf("failed to parse valoper address: %w", err)
}
ctx := context.Background()
valAddr, err := sdk.ValAddressFromBech32(validatorAddr)
if err != nil {
return "",fmt.Errorf("failed to parse validator address: %w", err)
}
validatorRes, err := stakingClient.GetValidator(ctx, valAddr.String())
if err != nil || validatorRes == nil || validatorRes.GetValidator().ConsensusPubkey == nil {
return "", fmt.Errorf("failed to get validator: %w", err)
}
pubKeyBytes := validatorRes.GetValidator().ConsensusPubkey.Value[2:]
hash := sha256.Sum256(pubKeyBytes)
consAddr := hash[:20]
bech32ConsAddr, err := sdk.Bech32ifyAddressBytes("cosmosvalcons", consAddr)
if err != nil {
return "", fmt.Errorf("failed to convert consensus address: %w", err)
}
return bech32ConsAddr, nil
}
위의 코드는 GRPC를 통해 Staking Module의 GetValidator를 호출하였고, 응답값의 Public Key를 Bech32 Address로 변환 후 Validator Consensus Address를 구하는 방법으로 해당 Account Address의 Validator Consensus Address를 정상적으로 구할 수 있었습니다.
Outro
프로젝트에서 모니터링을 위하여 Slashing 모듈 GRPC 요청을 위해서는 Consensus Address가 필요했는데, 처음 방법으로는 아래와 같은 응답값만을 받았습니다.
grpcurl -plaintext \
-d '{"cons_address": "cosmosvalcons1..."}' \
cosmos-grpc.polkachu.com:14990 \
cosmos.slashing.v1beta1.Query/SigningInfo
ERROR:
Code: NotFound
Message: SigningInfo not found for validator cosmosvalcons1...
이 에러를 해결하기 위해 코스모스의 주소 체계를 깊이 파고들었고, 그 과정에서 Application Layer와 Consensus Layer의 차이점, 각 주소 유형의 목적과 생성 방식까지 상세히 이해할 수 있었습니다. 이 글이 코스모스 생태계의 주소 체계를 이해하는 데 도움이 되었길 바랍니다.