sdk: add new governance VAA for IbcReceiverUpdateChainConnection (#2590)

* sdk: add new governance VAA for IbcReceiverUpdateChainConnection

* Enforce connectionId to be 64 bytes, add tests in rust sdk

* Update PrependBufferBytesFixed to LeftPadBytes, add template function + command for IBC governance VAA

* Add >64 length check for ibcReceiverUpdateChainConnectionConnectionId in runIbcReceiverUpdateChainConnectionTemplate command

* Update naming of governance VAA to reflect new mapping of channelId -> chainId

* Add TargetChainID to admin commands

* Node: Add IBC update channel to admin verify cmd

---------

Co-authored-by: Bruce Riley <briley@jumptrading.com>
This commit is contained in:
Nikhil Suri 2023-05-12 03:11:21 -07:00 committed by GitHub
parent df796930b1
commit ee7d0765f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1057 additions and 436 deletions

View File

@ -360,6 +360,38 @@ func circleIntegrationUpgradeContractImplementation(req *nodev1.CircleIntegratio
return v, nil
}
func ibcReceiverUpdateChannelChain(
req *nodev1.IbcReceiverUpdateChannelChain,
timestamp time.Time,
guardianSetIndex uint32,
nonce uint32,
sequence uint64,
) (*vaa.VAA, error) {
// validate parameters
if req.TargetChainId > math.MaxUint16 {
return nil, fmt.Errorf("invalid target chain id, must be <= %d", math.MaxUint16)
}
if req.ChainId > math.MaxUint16 {
return nil, fmt.Errorf("invalid chain id, must be <= %d", math.MaxUint16)
}
if len(req.ChannelId) > 64 {
return nil, fmt.Errorf("invalid channel ID length, must be <= 64")
}
channelId := vaa.LeftPadIbcChannelId(req.ChannelId)
// create governance VAA
v := vaa.CreateGovernanceVAA(timestamp, nonce, sequence, guardianSetIndex,
vaa.BodyIbcReceiverUpdateChannelChain{
TargetChainId: vaa.ChainID(req.TargetChainId),
ChannelId: channelId,
ChainId: vaa.ChainID(req.ChainId),
}.Serialize())
return v, nil
}
func (s *nodePrivilegedService) InjectGovernanceVAA(ctx context.Context, req *nodev1.InjectGovernanceVAARequest) (*nodev1.InjectGovernanceVAAResponse, error) {
s.logger.Info("governance VAA injected via admin socket", zap.String("request", req.String()))
@ -396,6 +428,8 @@ func (s *nodePrivilegedService) InjectGovernanceVAA(ctx context.Context, req *no
v, err = circleIntegrationRegisterEmitterAndDomain(payload.CircleIntegrationRegisterEmitterAndDomain, timestamp, req.CurrentSetIndex, message.Nonce, message.Sequence)
case *nodev1.GovernanceMessage_CircleIntegrationUpgradeContractImplementation:
v, err = circleIntegrationUpgradeContractImplementation(payload.CircleIntegrationUpgradeContractImplementation, timestamp, req.CurrentSetIndex, message.Nonce, message.Sequence)
case *nodev1.GovernanceMessage_IbcReceiverUpdateChannelChain:
v, err = ibcReceiverUpdateChannelChain(payload.IbcReceiverUpdateChannelChain, timestamp, req.CurrentSetIndex, message.Nonce, message.Sequence)
default:
panic(fmt.Sprintf("unsupported VAA type: %T", payload))
}

View File

@ -38,6 +38,10 @@ var circleIntegrationForeignEmitterAddress *string
var circleIntegrationCircleDomain *string
var circleIntegrationNewImplementationAddress *string
var ibcReceiverUpdateChannelChainTargetChainId *string
var ibcReceiverUpdateChannelChainChannelId *string
var ibcReceiverUpdateChannelChainChainId *string
func init() {
governanceFlagSet := pflag.NewFlagSet("governance", pflag.ExitOnError)
chainID = governanceFlagSet.String("chain-id", "", "Chain ID")
@ -91,6 +95,14 @@ func init() {
AdminClientCircleIntegrationUpgradeContractImplementationCmd.Flags().AddFlagSet(circleIntegrationChainIDFlagSet)
AdminClientCircleIntegrationUpgradeContractImplementationCmd.Flags().AddFlagSet(circleIntegrationUpgradeContractImplementationFlagSet)
TemplateCmd.AddCommand(AdminClientCircleIntegrationUpgradeContractImplementationCmd)
// flags for the ibc-receiver-update-channel-chain command
ibcReceiverUpdateChannelChainFlagSet := pflag.NewFlagSet("ibc-mapping", pflag.ExitOnError)
ibcReceiverUpdateChannelChainTargetChainId = ibcReceiverUpdateChannelChainFlagSet.String("target-chain-id", "", "Target Chain ID for the governance VAA")
ibcReceiverUpdateChannelChainChannelId = ibcReceiverUpdateChannelChainFlagSet.String("channel-id", "", "IBC Channel ID on Wormchain")
ibcReceiverUpdateChannelChainChainId = ibcReceiverUpdateChannelChainFlagSet.String("chain-id", "", "IBC Chain ID that the channel ID corresponds to")
AdminClientIbcReceiverUpdateChannelChainCmd.Flags().AddFlagSet(ibcReceiverUpdateChannelChainFlagSet)
TemplateCmd.AddCommand(AdminClientIbcReceiverUpdateChannelChainCmd)
}
var TemplateCmd = &cobra.Command{
@ -145,6 +157,12 @@ var AdminClientCircleIntegrationUpgradeContractImplementationCmd = &cobra.Comman
Run: runCircleIntegrationUpgradeContractImplementationTemplate,
}
var AdminClientIbcReceiverUpdateChannelChainCmd = &cobra.Command{
Use: "ibc-receiver-update-channel-chain",
Short: "Generate an empty ibc receiver channelId to chainId mapping update template at specified path",
Run: runIbcReceiverUpdateChannelChainTemplate,
}
func runGuardianSetTemplate(cmd *cobra.Command, args []string) {
// Use deterministic devnet addresses as examples in the template, such that this doubles as a test fixture.
guardians := make([]*nodev1.GuardianSetUpdate_Guardian, *setUpdateNumGuardians)
@ -454,6 +472,54 @@ func runCircleIntegrationUpgradeContractImplementationTemplate(cmd *cobra.Comman
fmt.Print(string(b))
}
func runIbcReceiverUpdateChannelChainTemplate(cmd *cobra.Command, args []string) {
if *ibcReceiverUpdateChannelChainTargetChainId == "" {
log.Fatal("--target-chain-id must be specified")
}
targetChainId, err := parseChainID(*ibcReceiverUpdateChannelChainTargetChainId)
if err != nil {
log.Fatal("failed to parse chain id: ", err)
}
if *ibcReceiverUpdateChannelChainChannelId == "" {
log.Fatal("--channel-id must be specified")
}
if len(*ibcReceiverUpdateChannelChainChannelId) > 64 {
log.Fatal("invalid channel id length, must be <= 64")
}
if *ibcReceiverUpdateChannelChainChainId == "" {
log.Fatal("--chain-id must be specified")
}
chainId, err := parseChainID(*ibcReceiverUpdateChannelChainChainId)
if err != nil {
log.Fatal("failed to parse chain id: ", err)
}
m := &nodev1.InjectGovernanceVAARequest{
CurrentSetIndex: uint32(*templateGuardianIndex),
Messages: []*nodev1.GovernanceMessage{
{
Sequence: rand.Uint64(),
Nonce: rand.Uint32(),
Payload: &nodev1.GovernanceMessage_IbcReceiverUpdateChannelChain{
IbcReceiverUpdateChannelChain: &nodev1.IbcReceiverUpdateChannelChain{
TargetChainId: uint32(targetChainId),
ChannelId: *ibcReceiverUpdateChannelChainChannelId,
ChainId: uint32(chainId),
},
},
},
},
}
b, err := prototext.MarshalOptions{Multiline: true}.Marshal(m)
if err != nil {
panic(err)
}
fmt.Print(string(b))
}
// parseAddress parses either a hex-encoded address and returns
// a left-padded 32 byte hex string.
func parseAddress(s string) (string, error) {

View File

@ -67,6 +67,8 @@ func runGovernanceVAAVerify(cmd *cobra.Command, args []string) {
v, err = circleIntegrationRegisterEmitterAndDomain(payload.CircleIntegrationRegisterEmitterAndDomain, timestamp, req.CurrentSetIndex, message.Nonce, message.Sequence)
case *nodev1.GovernanceMessage_CircleIntegrationUpgradeContractImplementation:
v, err = circleIntegrationUpgradeContractImplementation(payload.CircleIntegrationUpgradeContractImplementation, timestamp, req.CurrentSetIndex, message.Nonce, message.Sequence)
case *nodev1.GovernanceMessage_IbcReceiverUpdateChannelChain:
v, err = ibcReceiverUpdateChannelChain(payload.IbcReceiverUpdateChannelChain, timestamp, req.CurrentSetIndex, message.Nonce, message.Sequence)
default:
panic(fmt.Sprintf("unsupported VAA type: %T", payload))
}

File diff suppressed because it is too large Load Diff

View File

@ -100,6 +100,9 @@ message GovernanceMessage {
CircleIntegrationUpdateWormholeFinality circle_integration_update_wormhole_finality = 18;
CircleIntegrationRegisterEmitterAndDomain circle_integration_register_emitter_and_domain = 19;
CircleIntegrationUpgradeContractImplementation circle_integration_upgrade_contract_implementation = 20;
// IBC Receiver Integration
IbcReceiverUpdateChannelChain ibc_receiver_update_channel_chain = 21;
}
}
@ -236,6 +239,15 @@ message CircleIntegrationUpgradeContractImplementation {
uint32 target_chain_id = 2;
}
message IbcReceiverUpdateChannelChain {
// Chain ID that this governance VAA should be redeemed on
uint32 target_chain_id = 1;
// IBC channel ID
string channel_id = 2;
// ChainID corresponding to the IBC channel
uint32 chain_id = 3;
}
message FindMissingMessagesRequest {
// Emitter chain ID to iterate.
uint32 emitter_chain = 1;

View File

@ -0,0 +1,331 @@
use serde::{Deserialize, Serialize};
use crate::Chain;
/// Represents a governance action targeted at the wormchain ibc receiver contract.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Action {
#[serde(rename = "1")]
UpdateChannelChain {
// an existing IBC channel ID
#[serde(with = "crate::serde_array")]
channel_id: [u8; 64],
// the chain associated with this IBC channel_id
chain_id: Chain,
},
}
// MODULE = "IbcReceiver"
pub const MODULE: [u8; 32] = *b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00IbcReceiver";
/// Represents the payload for a governance VAA targeted at the wormchain ibc receiver contract.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GovernancePacket {
/// Describes the chain on which the governance action should be carried out.
pub chain: Chain,
/// The actual governance action to be carried out.
pub action: Action,
}
mod governance_packet_impl {
use std::fmt;
use serde::{
de::{Error, MapAccess, SeqAccess, Visitor},
ser::SerializeStruct,
Deserialize, Deserializer, Serialize, Serializer,
};
use crate::{
ibc_receiver::{Action, GovernancePacket, MODULE},
Chain,
};
struct Module;
impl Serialize for Module {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
MODULE.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Module {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let arr = <[u8; 32]>::deserialize(deserializer)?;
if arr == MODULE {
Ok(Module)
} else {
Err(Error::custom(
"invalid governance module, expected \"IbcReceiver\"",
))
}
}
}
// governance actions
#[derive(Serialize, Deserialize)]
struct UpdateChannelChain {
#[serde(with = "crate::serde_array")]
channel_id: [u8; 64],
chain_id: Chain,
}
impl Serialize for GovernancePacket {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_struct("GovernancePacket", 4)?;
seq.serialize_field("module", &Module)?;
// The wire format encodes the action before the chain and then appends the actual
// action payload.
match self.action.clone() {
Action::UpdateChannelChain {
channel_id,
chain_id,
} => {
seq.serialize_field("action", &1u8)?;
seq.serialize_field("chain", &self.chain)?;
seq.serialize_field(
"payload",
&UpdateChannelChain {
channel_id,
chain_id,
},
)?;
}
}
seq.end()
}
}
struct GovernancePacketVisitor;
impl<'de> Visitor<'de> for GovernancePacketVisitor {
type Value = GovernancePacket;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("struct GovernancePacket")
}
#[inline]
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
static EXPECTING: &str = "struct GovernancePacket with 4 elements";
let _: Module = seq
.next_element()?
.ok_or_else(|| Error::invalid_length(0, &EXPECTING))?;
let act: u8 = seq
.next_element()?
.ok_or_else(|| Error::invalid_length(1, &EXPECTING))?;
let chain = seq
.next_element()?
.ok_or_else(|| Error::invalid_length(2, &EXPECTING))?;
let action = match act {
1 => {
let UpdateChannelChain {
channel_id,
chain_id,
} = seq
.next_element()?
.ok_or_else(|| Error::invalid_length(3, &EXPECTING))?;
Action::UpdateChannelChain {
channel_id,
chain_id,
}
}
v => {
return Err(Error::custom(format_args!(
"invalid value: {v}, expected 1"
)))
}
};
Ok(GovernancePacket { chain, action })
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
enum Field {
Module,
Action,
Chain,
Payload,
}
let mut module = None;
let mut chain = None;
let mut action = None;
let mut payload = None;
while let Some(key) = map.next_key::<Field>()? {
match key {
Field::Module => {
if module.is_some() {
return Err(Error::duplicate_field("module"));
}
module = map.next_value::<Module>().map(Some)?;
}
Field::Action => {
if action.is_some() {
return Err(Error::duplicate_field("action"));
}
action = map.next_value::<u8>().map(Some)?;
}
Field::Chain => {
if chain.is_some() {
return Err(Error::duplicate_field("chain"));
}
chain = map.next_value().map(Some)?;
}
Field::Payload => {
if payload.is_some() {
return Err(Error::duplicate_field("payload"));
}
let a = action.as_ref().copied().ok_or_else(|| {
Error::custom("`action` must be known before deserializing `payload`")
})?;
let p = match a {
1 => {
let UpdateChannelChain {
channel_id,
chain_id,
} = map.next_value()?;
Action::UpdateChannelChain {
channel_id,
chain_id,
}
}
v => {
return Err(Error::custom(format_args!(
"invalid action: {v}, expected one of: 1, 2"
)))
}
};
payload = Some(p);
}
}
}
let _ = module.ok_or_else(|| Error::missing_field("module"))?;
let chain = chain.ok_or_else(|| Error::missing_field("chain"))?;
let action = payload.ok_or_else(|| Error::missing_field("payload"))?;
Ok(GovernancePacket { chain, action })
}
}
impl<'de> Deserialize<'de> for GovernancePacket {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
const FIELDS: &[&str] = &["module", "action", "chain", "payload"];
deserializer.deserialize_struct("GovernancePacket", FIELDS, GovernancePacketVisitor)
}
}
}
#[cfg(test)]
mod test {
use crate::{vaa::Signature, Chain, Vaa, GOVERNANCE_EMITTER};
use super::{Action, GovernancePacket};
#[test]
fn happy_path() {
let buf = [
// version
0x01, // guardian set index
0x00, // signatures
0x00, 0x00, 0x00, 0x01, 0x00, 0xb0, 0x72, 0x50, 0x5b, 0x5b, 0x99, 0x9c, 0x1d, 0x08,
0x90, 0x5c, 0x02, 0xe2, 0xb6, 0xb2, 0x83, 0x2e, 0xf7, 0x2c, 0x0b, 0xa6, 0xc8, 0xdb,
0x4f, 0x77, 0xfe, 0x45, 0x7e, 0xf2, 0xb3, 0xd0, 0x53, 0x41, 0x0b, 0x1e, 0x92, 0xa9,
0x19, 0x4d, 0x92, 0x10, 0xdf, 0x24, 0xd9, 0x87, 0xac, 0x83, 0xd7, 0xb6, 0xf0, 0xc2,
0x1c, 0xe9, 0x0f, 0x8b, 0xc1, 0x86, 0x9d, 0xe0, 0x89, 0x8b, 0xda, 0x7e, 0x98, 0x01,
// timestamp
0x00, 0x00, 0x00, 0x01, // nonce
0x00, 0x00, 0x00, 0x01, // emitter chain
0x00, 0x01, // emitter address
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x04, // sequence
0x00, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x1b, 0xfa, // consistency
0x00, // module = "IbcReceiver"
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x62, 0x63, 0x52, 0x65, 0x63, 0x65,
0x69, 0x76, 0x65, 0x72, // action (IbcReceiverActionUpdateChannelChain)
0x01, // target chain_id (unset)
0x00, 0x00, // IBC channel_id for the mapping ("channel-0")
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63,
0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x2d, 0x30, // IBC chain_id for the mapping
0x00, 0x13,
];
let channel_id_bytes: [u8; 64] =
*b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00channel-0";
let vaa = Vaa {
version: 1,
guardian_set_index: 0,
signatures: vec![Signature {
index: 0,
signature: [
0xb0, 0x72, 0x50, 0x5b, 0x5b, 0x99, 0x9c, 0x1d, 0x08, 0x90, 0x5c, 0x02, 0xe2,
0xb6, 0xb2, 0x83, 0x2e, 0xf7, 0x2c, 0x0b, 0xa6, 0xc8, 0xdb, 0x4f, 0x77, 0xfe,
0x45, 0x7e, 0xf2, 0xb3, 0xd0, 0x53, 0x41, 0x0b, 0x1e, 0x92, 0xa9, 0x19, 0x4d,
0x92, 0x10, 0xdf, 0x24, 0xd9, 0x87, 0xac, 0x83, 0xd7, 0xb6, 0xf0, 0xc2, 0x1c,
0xe9, 0x0f, 0x8b, 0xc1, 0x86, 0x9d, 0xe0, 0x89, 0x8b, 0xda, 0x7e, 0x98, 0x01,
],
}],
timestamp: 1,
nonce: 1,
emitter_chain: Chain::Solana,
emitter_address: GOVERNANCE_EMITTER,
sequence: 20_716_538,
consistency_level: 0,
payload: GovernancePacket {
chain: Chain::Any,
action: Action::UpdateChannelChain {
channel_id: channel_id_bytes,
chain_id: Chain::Injective,
},
},
};
assert_eq!(buf.as_ref(), &serde_wormhole::to_vec(&vaa).unwrap());
assert_eq!(vaa, serde_wormhole::from_slice(&buf).unwrap());
let encoded = serde_json::to_string(&vaa).unwrap();
assert_eq!(vaa, serde_json::from_str(&encoded).unwrap());
}
}

View File

@ -19,6 +19,7 @@ pub mod accountant;
mod arraystring;
mod chain;
pub mod core;
pub mod ibc_receiver;
pub mod nft;
mod serde_array;
pub mod token;

View File

@ -3,6 +3,7 @@ package vaa
import (
"bytes"
"encoding/binary"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
@ -23,6 +24,14 @@ var CircleIntegrationModule = [32]byte{
}
var CircleIntegrationModuleStr = string(CircleIntegrationModule[:])
// WasmdModule is the identifier of the Wormchain ibc_receiver contract module (which is used for governance messages)
// It is the hex representation of "IbcReceiver" left padded with zeroes.
var IbcReceiverModule = [32]byte{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x62, 0x63, 0x52, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x72,
}
var IbcReceiverModuleStr = string(IbcReceiverModule[:])
type GovernanceAction uint8
var (
@ -51,6 +60,9 @@ var (
CircleIntegrationActionUpdateWormholeFinality GovernanceAction = 1
CircleIntegrationActionRegisterEmitterAndDomain GovernanceAction = 2
CircleIntegrationActionUpgradeContractImplementation GovernanceAction = 3
// Ibc Receiver governance actions
IbcReceiverActionUpdateChannelChain GovernanceAction = 1
)
type (
@ -127,6 +139,17 @@ type (
TargetChainID ChainID
NewImplementationAddress [32]byte
}
// BodyIbcReceiverUpdateChannelChain is a governance message to update the ibc channel_id -> chain_id mapping in the ibc_receiver contract
BodyIbcReceiverUpdateChannelChain struct {
// The chain that this governance VAA should be redeemed on
TargetChainId ChainID
// This should follow the IBC channel identifier standard: https://github.com/cosmos/ibc/tree/main/spec/core/ics-024-host-requirements#paths-identifiers-separators
// If the identifier string is shorter than 64 bytes, the correct number of 0x00 bytes should be prepended.
ChannelId [64]byte
ChainId ChainID
}
)
func (b BodyContractUpgrade) Serialize() []byte {
@ -228,18 +251,15 @@ func (r BodyCircleIntegrationUpgradeContractImplementation) Serialize() []byte {
return serializeBridgeGovernanceVaa(CircleIntegrationModuleStr, CircleIntegrationActionUpgradeContractImplementation, r.TargetChainID, payload.Bytes())
}
func (r BodyIbcReceiverUpdateChannelChain) Serialize() []byte {
payload := &bytes.Buffer{}
payload.Write(r.ChannelId[:])
MustWrite(payload, binary.BigEndian, r.ChainId)
return serializeBridgeGovernanceVaa(IbcReceiverModuleStr, IbcReceiverActionUpdateChannelChain, r.TargetChainId, payload.Bytes())
}
func serializeBridgeGovernanceVaa(module string, actionId GovernanceAction, chainId ChainID, payload []byte) []byte {
if len(module) > 32 {
panic("module longer than 32 byte")
}
buf := &bytes.Buffer{}
// Write token bridge header
for i := 0; i < (32 - len(module)); i++ {
buf.WriteByte(0x00)
}
buf.Write([]byte(module))
buf := LeftPadBytes(module, 32)
// Write action ID
MustWrite(buf, binary.BigEndian, actionId)
// Write target chain
@ -249,3 +269,33 @@ func serializeBridgeGovernanceVaa(module string, actionId GovernanceAction, chai
return buf.Bytes()
}
func LeftPadIbcChannelId(channelId string) [64]byte {
channelIdBuf := LeftPadBytes(channelId, 64)
var channelIdIdLeftPadded [64]byte
copy(channelIdIdLeftPadded[:], channelIdBuf.Bytes())
return channelIdIdLeftPadded
}
// Prepends 0x00 bytes to the payload buffer, up to a size of `length`
func LeftPadBytes(payload string, length int) *bytes.Buffer {
if length < 0 {
panic("cannot prepend bytes to a negative length buffer")
}
if len(payload) > length {
panic(fmt.Sprintf("payload longer than %d bytes", length))
}
buf := &bytes.Buffer{}
// Prepend correct number of 0x00 bytes to the payload slice
for i := 0; i < (length - len(payload)); i++ {
buf.WriteByte(0x00)
}
// add the payload slice
buf.Write([]byte(payload))
return buf
}

View File

@ -84,7 +84,7 @@ func TestBodyTokenBridgeRegisterChainSerialize(t *testing.T) {
name: "panic_at_the_disco!",
panic: true,
object: BodyTokenBridgeRegisterChain{Module: "123456789012345678901234567890123", ChainID: 1, EmitterAddress: addr},
expected: "module longer than 32 byte",
expected: "payload longer than 32 bytes",
},
}
for _, testCase := range tests {
@ -149,3 +149,16 @@ func TestBodyCircleIntegrationUpgradeContractImplementationSerialize(t *testing.
}
assert.Equal(t, expected, hex.EncodeToString(bodyCircleIntegrationUpgradeContractImplementation.Serialize()))
}
func TestBodyIbcReceiverUpdateChannelChain(t *testing.T) {
expected := "0000000000000000000000000000000000000000004962635265636569766572010c20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006368616e6e656c2d300013"
channelId := LeftPadIbcChannelId("channel-0")
bodyIbcReceiverUpdateChannelChain := BodyIbcReceiverUpdateChannelChain{
TargetChainId: ChainIDWormchain,
ChannelId: channelId,
ChainId: ChainIDInjective,
}
assert.Equal(t, expected, hex.EncodeToString(bodyIbcReceiverUpdateChannelChain.Serialize()))
}