Struct avalanche_types::txs::Tx
source · pub struct Tx {
pub metadata: Option<Metadata>,
pub network_id: u32,
pub blockchain_id: Id,
pub transferable_outputs: Option<Vec<Output>>,
pub transferable_inputs: Option<Vec<Input>>,
pub memo: Option<Vec<u8>>,
}
Expand description
Fields§
§metadata: Option<Metadata>
§network_id: u32
§blockchain_id: Id
§transferable_outputs: Option<Vec<Output>>
§transferable_inputs: Option<Vec<Input>>
§memo: Option<Vec<u8>>
Implementations§
source§impl Tx
impl Tx
sourcepub fn default() -> Self
pub fn default() -> Self
Examples found in repository?
More examples
pub fn type_id() -> u32
sourcepub fn pack(&self, codec_version: u16, type_id: u32) -> Result<Packer>
pub fn pack(&self, codec_version: u16, type_id: u32) -> Result<Packer>
“Tx.Unsigned” is implemented by “avax.BaseTx” but for marshal, it’s passed as an interface. Then marshaled via “avalanchego/codec/linearcodec.linearCodec” which then calls “genericCodec.marshal”. ref. “avalanchego/vms/avm.Tx.SignSECP256K1Fx” ref. “avalanchego/codec.manager.Marshal” ref. “avalanchego/codec.manager.Marshal(codecVersion, &t.UnsignedTx)” ref. “avalanchego/codec/linearcodec.linearCodec.MarshalInto” ref. “avalanchego/codec/reflectcodec.genericCodec.MarshalInto” ref. “avalanchego/codec/reflectcodec.genericCodec.marshal”
Returns the packer itself so that the following marshals can reuse.
“BaseTx” is an interface in Go (reflect.Interface) thus the underlying type must be specified by the caller TODO: can we do better in Rust? Go does so with reflect… e.g., pack prefix with the type ID for “avm.BaseTx” (linearCodec.PackPrefix) ref. “avalanchego/codec/linearcodec.linearCodec.MarshalInto” ref. “avalanchego/codec/reflectcodec.genericCodec.MarshalInto”
Examples found in repository?
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
pub async fn sign<T: key::secp256k1::SignOnly>(
&mut self,
signers: Vec<Vec<T>>,
) -> io::Result<()> {
// marshal "unsigned tx" with the codec version
let type_id = Self::type_id();
let packer = self.base_tx.pack(codec::VERSION, type_id)?;
// "avalanchego" marshals the whole struct again for signed bytes
// even when the underlying "unsigned_tx" is already once marshaled
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/avm/txs#Tx.SignSECP256K1Fx
//
// reuse the underlying packer to avoid marshaling the unsigned tx twice
// just marshal the next fields in the struct and pack them all together
// in the existing packer
let tx_bytes_with_no_signature = packer.take_bytes();
packer.set_bytes(&tx_bytes_with_no_signature);
// compute sha256 for marshaled "unsigned tx" bytes
// IMPORTANT: take the hash only for the type "avm.Tx" unsigned tx
// not other fields -- only hash "avm.Tx.UnsignedTx" but not "avm.Tx.Creds"
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/avm#Tx
let tx_bytes_hash = hash::sha256(&tx_bytes_with_no_signature);
// number of of credentials
let fx_creds_len = signers.len() as u32;
// pack the second field in the struct
packer.pack_u32(fx_creds_len)?;
// sign the hash with the signers (in case of multi-sig)
// and combine all signatures into a secp256k1fx credential
self.fx_creds = Vec::new();
for keys in signers.iter() {
let mut sigs: Vec<Vec<u8>> = Vec::new();
for k in keys.iter() {
let sig = k.sign_digest(&tx_bytes_hash).await.map_err(|e| {
Error::new(ErrorKind::Other, format!("failed sign_digest {}", e))
})?;
sigs.push(Vec::from(sig));
}
let mut cred = key::secp256k1::txs::Credential::default();
cred.signatures = sigs;
let mut fx_cred = fx::Credential::default();
fx_cred.cred = cred;
// add a new credential to "Tx"
self.fx_creds.push(fx_cred);
}
if fx_creds_len > 0 {
// pack each "fx_cred" which is "secp256k1fx.Credential"
// marshal type ID for "secp256k1fx.Credential"
let cred_type_id = key::secp256k1::txs::Credential::type_id();
for fx_cred in self.fx_creds.iter() {
packer.pack_u32(cred_type_id)?;
packer.pack_u32(fx_cred.cred.signatures.len() as u32)?;
for sig in fx_cred.cred.signatures.iter() {
packer.pack_bytes(sig)?;
}
}
}
let tx_bytes_with_signatures = packer.take_bytes();
let tx_id = hash::sha256(&tx_bytes_with_signatures);
// update "BaseTx.Metadata" with id/unsigned bytes/bytes
// ref. "avalanchego/vms/avm.Tx.SignSECP256K1Fx"
// ref. "avalanchego/vms/components/avax.BaseTx.Metadata.Initialize"
self.base_tx.metadata = Some(txs::Metadata {
id: ids::Id::from_slice(&tx_id),
tx_bytes_with_no_signature: tx_bytes_with_no_signature.to_vec(),
tx_bytes_with_signatures: tx_bytes_with_signatures.to_vec(),
});
Ok(())
}
More examples
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
pub async fn sign<T: key::secp256k1::SignOnly>(
&mut self,
signers: Vec<Vec<T>>,
) -> io::Result<()> {
// marshal "unsigned tx" with the codec version
let type_id = Self::type_id();
let packer = self.base_tx.pack(codec::VERSION, type_id)?;
// "avalanchego" marshals the whole struct again for signed bytes
// even when the underlying "unsigned_tx" is already once marshaled
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#Tx.Sign
//
// reuse the underlying packer to avoid marshaling the unsigned tx twice
// just marshal the next fields in the struct and pack them all together
// in the existing packer
let base = packer.take_bytes();
packer.set_bytes(&base);
// pack the second field "owner" in the struct
// not embedded thus encode struct type id
let output_owners_type_id = key::secp256k1::txs::OutputOwners::type_id();
packer.pack_u32(output_owners_type_id)?;
packer.pack_u64(self.owner.locktime)?;
packer.pack_u32(self.owner.threshold)?;
packer.pack_u32(self.owner.addresses.len() as u32)?;
for addr in self.owner.addresses.iter() {
packer.pack_bytes(addr.as_ref())?;
}
// take bytes just for hashing computation
let tx_bytes_with_no_signature = packer.take_bytes();
packer.set_bytes(&tx_bytes_with_no_signature);
// compute sha256 for marshaled "unsigned tx" bytes
// IMPORTANT: take the hash only for the type "platformvm.UnsignedAddValidatorTx" unsigned tx
// not other fields -- only hash "platformvm.UnsignedAddValidatorTx.*" but not "platformvm.Tx.Creds"
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#UnsignedAddValidatorTx
let tx_bytes_hash = hash::sha256(&tx_bytes_with_no_signature);
// number of of credentials
let creds_len = signers.len() as u32;
// pack the fourth field in the struct
packer.pack_u32(creds_len)?;
// sign the hash with the signers (in case of multi-sig)
// and combine all signatures into a secp256k1fx credential
self.creds = Vec::new();
for keys in signers.iter() {
let mut sigs: Vec<Vec<u8>> = Vec::new();
for k in keys.iter() {
let sig = k.sign_digest(&tx_bytes_hash).await.map_err(|e| {
Error::new(ErrorKind::Other, format!("failed sign_digest {}", e))
})?;
sigs.push(Vec::from(sig));
}
let mut cred = key::secp256k1::txs::Credential::default();
cred.signatures = sigs;
// add a new credential to "Tx"
self.creds.push(cred);
}
if creds_len > 0 {
// pack each "cred" which is "secp256k1fx.Credential"
// marshal type ID for "secp256k1fx.Credential"
let cred_type_id = key::secp256k1::txs::Credential::type_id();
for cred in self.creds.iter() {
// marshal type ID for "secp256k1fx.Credential"
packer.pack_u32(cred_type_id)?;
// marshal fields for "secp256k1fx.Credential"
packer.pack_u32(cred.signatures.len() as u32)?;
for sig in cred.signatures.iter() {
packer.pack_bytes(sig)?;
}
}
}
let tx_bytes_with_signatures = packer.take_bytes();
let tx_id = hash::sha256(&tx_bytes_with_signatures);
// update "BaseTx.Metadata" with id/unsigned bytes/bytes
// ref. "avalanchego/vms/platformvm.Tx.Sign"
// ref. "avalanchego/vms/components/avax.BaseTx.Metadata.Initialize"
self.base_tx.metadata = Some(txs::Metadata {
id: ids::Id::from_slice(&tx_id),
tx_bytes_with_no_signature: tx_bytes_with_no_signature.to_vec(),
tx_bytes_with_signatures: tx_bytes_with_signatures.to_vec(),
});
Ok(())
}
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
pub async fn sign<T: key::secp256k1::SignOnly>(
&mut self,
signers: Vec<Vec<T>>,
) -> io::Result<()> {
// marshal "unsigned tx" with the codec version
let type_id = Self::type_id();
let packer = self.base_tx.pack(codec::VERSION, type_id)?;
// "avalanchego" marshals the whole struct again for signed bytes
// even when the underlying "unsigned_tx" is already once marshaled
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#Tx.Sign
//
// reuse the underlying packer to avoid marshaling the unsigned tx twice
// just marshal the next fields in the struct and pack them all together
// in the existing packer
let unsigned_tx_bytes = packer.take_bytes();
packer.set_bytes(&unsigned_tx_bytes);
// pack the second field "validator" in the struct
packer.pack_bytes(self.validator.validator.node_id.as_ref())?;
packer.pack_u64(self.validator.validator.start)?;
packer.pack_u64(self.validator.validator.end)?;
packer.pack_u64(self.validator.validator.weight)?;
packer.pack_bytes(self.validator.subnet_id.as_ref())?;
// pack the third field "subnet_auth" in the struct
let subnet_auth_type_id = key::secp256k1::txs::Input::type_id();
packer.pack_u32(subnet_auth_type_id)?;
packer.pack_u32(self.subnet_auth.sig_indices.len() as u32)?;
for sig_idx in self.subnet_auth.sig_indices.iter() {
packer.pack_u32(*sig_idx)?;
}
// take bytes just for hashing computation
let tx_bytes_with_no_signature = packer.take_bytes();
packer.set_bytes(&tx_bytes_with_no_signature);
// compute sha256 for marshaled "unsigned tx" bytes
// IMPORTANT: take the hash only for the type "platformvm.UnsignedAddValidatorTx" unsigned tx
// not other fields -- only hash "platformvm.UnsignedAddValidatorTx.*" but not "platformvm.Tx.Creds"
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#UnsignedAddValidatorTx
let tx_bytes_hash = hash::sha256(&tx_bytes_with_no_signature);
// number of of credentials
let creds_len = signers.len() as u32;
// pack the fourth field in the struct
packer.pack_u32(creds_len)?;
// sign the hash with the signers (in case of multi-sig)
// and combine all signatures into a secp256k1fx credential
self.creds = Vec::new();
for keys in signers.iter() {
let mut sigs: Vec<Vec<u8>> = Vec::new();
for k in keys.iter() {
let sig = k.sign_digest(&tx_bytes_hash).await.map_err(|e| {
Error::new(ErrorKind::Other, format!("failed sign_digest {}", e))
})?;
sigs.push(Vec::from(sig));
}
let mut cred = key::secp256k1::txs::Credential::default();
cred.signatures = sigs;
// add a new credential to "Tx"
self.creds.push(cred);
}
if creds_len > 0 {
// pack each "cred" which is "secp256k1fx.Credential"
// marshal type ID for "secp256k1fx.Credential"
let cred_type_id = key::secp256k1::txs::Credential::type_id();
for cred in self.creds.iter() {
// marshal type ID for "secp256k1fx.Credential"
packer.pack_u32(cred_type_id)?;
// marshal fields for "secp256k1fx.Credential"
packer.pack_u32(cred.signatures.len() as u32)?;
for sig in cred.signatures.iter() {
packer.pack_bytes(sig)?;
}
}
}
let tx_bytes_with_signatures = packer.take_bytes();
let tx_id = hash::sha256(&tx_bytes_with_signatures);
// update "BaseTx.Metadata" with id/unsigned bytes/bytes
// ref. "avalanchego/vms/platformvm.Tx.Sign"
// ref. "avalanchego/vms/components/avax.BaseTx.Metadata.Initialize"
self.base_tx.metadata = Some(txs::Metadata {
id: ids::Id::from_slice(&tx_id),
tx_bytes_with_no_signature: tx_bytes_with_no_signature.to_vec(),
tx_bytes_with_signatures: tx_bytes_with_signatures.to_vec(),
});
Ok(())
}
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
pub async fn sign<T: key::secp256k1::SignOnly>(
&mut self,
signers: Vec<Vec<T>>,
) -> io::Result<()> {
// marshal "unsigned tx" with the codec version
let type_id = Self::type_id();
let packer = self.base_tx.pack(codec::VERSION, type_id)?;
// "avalanchego" marshals the whole struct again for signed bytes
// even when the underlying "unsigned_tx" is already once marshaled
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#Tx.Sign
//
// reuse the underlying packer to avoid marshaling the unsigned tx twice
// just marshal the next fields in the struct and pack them all together
// in the existing packer
let base = packer.take_bytes();
packer.set_bytes(&base);
// pack the second field "subnet_id" in the struct
packer.pack_bytes(self.subnet_id.as_ref())?;
// pack the third field "chain_name" in the struct
packer.pack_str(&self.chain_name)?;
// pack the fourth field "vm_id" in the struct
packer.pack_bytes(self.vm_id.as_ref())?;
// pack the fifth field "fx_ids" in the struct
if self.fx_ids.is_some() {
let fx_ids = self.fx_ids.as_ref().unwrap();
packer.pack_u32(fx_ids.len() as u32)?;
for fx_id in fx_ids.iter() {
packer.pack_bytes(fx_id.as_ref())?;
}
} else {
packer.pack_u32(0_u32)?;
}
// pack the sixth field "genesis_data" in the struct
// []byte is reflected as "reflect.Slice" in avalanchego
// thus encode its length
packer.pack_u32(self.genesis_data.len() as u32)?;
packer.pack_bytes(&self.genesis_data)?;
// pack the seventh field "subnet_auth" in the struct
let subnet_auth_type_id = key::secp256k1::txs::Input::type_id();
packer.pack_u32(subnet_auth_type_id)?;
packer.pack_u32(self.subnet_auth.sig_indices.len() as u32)?;
for sig_idx in self.subnet_auth.sig_indices.iter() {
packer.pack_u32(*sig_idx)?;
}
// take bytes just for hashing computation
let tx_bytes_with_no_signature = packer.take_bytes();
packer.set_bytes(&tx_bytes_with_no_signature);
// compute sha256 for marshaled "unsigned tx" bytes
// IMPORTANT: take the hash only for the type "platformvm.UnsignedAddValidatorTx" unsigned tx
// not other fields -- only hash "platformvm.UnsignedAddValidatorTx.*" but not "platformvm.Tx.Creds"
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#UnsignedAddValidatorTx
let tx_bytes_hash = hash::sha256(&tx_bytes_with_no_signature);
// number of of credentials
let creds_len = signers.len() as u32;
// pack the fourth field in the struct
packer.pack_u32(creds_len)?;
// sign the hash with the signers (in case of multi-sig)
// and combine all signatures into a secp256k1fx credential
self.creds = Vec::new();
for keys in signers.iter() {
let mut sigs: Vec<Vec<u8>> = Vec::new();
for k in keys.iter() {
let sig = k.sign_digest(&tx_bytes_hash).await.map_err(|e| {
Error::new(ErrorKind::Other, format!("failed sign_digest {}", e))
})?;
sigs.push(Vec::from(sig));
}
let mut cred = key::secp256k1::txs::Credential::default();
cred.signatures = sigs;
// add a new credential to "Tx"
self.creds.push(cred);
}
if creds_len > 0 {
// pack each "cred" which is "secp256k1fx.Credential"
// marshal type ID for "secp256k1fx.Credential"
let cred_type_id = key::secp256k1::txs::Credential::type_id();
for cred in self.creds.iter() {
// marshal type ID for "secp256k1fx.Credential"
packer.pack_u32(cred_type_id)?;
// marshal fields for "secp256k1fx.Credential"
packer.pack_u32(cred.signatures.len() as u32)?;
for sig in cred.signatures.iter() {
packer.pack_bytes(sig)?;
}
}
}
let tx_bytes_with_signatures = packer.take_bytes();
let tx_id = hash::sha256(&tx_bytes_with_signatures);
// update "BaseTx.Metadata" with id/unsigned bytes/bytes
// ref. "avalanchego/vms/platformvm.Tx.Sign"
// ref. "avalanchego/vms/components/avax.BaseTx.Metadata.Initialize"
self.base_tx.metadata = Some(txs::Metadata {
id: ids::Id::from_slice(&tx_id),
tx_bytes_with_no_signature: tx_bytes_with_no_signature.to_vec(),
tx_bytes_with_signatures: tx_bytes_with_signatures.to_vec(),
});
Ok(())
}
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
pub async fn sign<T: key::secp256k1::SignOnly>(
&mut self,
signers: Vec<Vec<T>>,
) -> io::Result<()> {
// marshal "unsigned tx" with the codec version
let type_id = Self::type_id();
let packer = self.base_tx.pack(codec::VERSION, type_id)?;
// "avalanchego" marshals the whole struct again for signed bytes
// even when the underlying "unsigned_tx" is already once marshaled
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#Tx.Sign
//
// reuse the underlying packer to avoid marshaling the unsigned tx twice
// just marshal the next fields in the struct and pack them all together
// in the existing packer
let base = packer.take_bytes();
packer.set_bytes(&base);
// pack the second field in the struct
packer.pack_bytes(self.destination_chain_id.as_ref())?;
// pack the third field in the struct
if self.destination_chain_transferable_outputs.is_some() {
let destination_chain_outs = self
.destination_chain_transferable_outputs
.as_ref()
.unwrap();
packer.pack_u32(destination_chain_outs.len() as u32)?;
for transferable_output in destination_chain_outs.iter() {
// "TransferableOutput.Asset" is struct and serialize:"true"
// but embedded inline in the struct "TransferableOutput"
// so no need to encode type ID
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/components/avax#TransferableOutput
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/components/avax#Asset
packer.pack_bytes(transferable_output.asset_id.as_ref())?;
// fx_id is serialize:"false" thus skipping serialization
// decide the type
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/components/avax#TransferableOutput
if transferable_output.transfer_output.is_none()
&& transferable_output.stakeable_lock_out.is_none()
{
return Err(Error::new(
ErrorKind::InvalidInput,
"unexpected Nones in TransferableOutput transfer_output and stakeable_lock_out",
));
}
let type_id_transferable_out = {
if transferable_output.transfer_output.is_some() {
key::secp256k1::txs::transfer::Output::type_id()
} else {
platformvm::txs::StakeableLockOut::type_id()
}
};
// marshal type ID for "key::secp256k1::txs::transfer::Output" or "platformvm::txs::StakeableLockOut"
packer.pack_u32(type_id_transferable_out)?;
match type_id_transferable_out {
7 => {
// "key::secp256k1::txs::transfer::Output"
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/secp256k1fx#TransferOutput
let transfer_output = transferable_output.transfer_output.clone().unwrap();
// marshal "secp256k1fx.TransferOutput.Amt" field
packer.pack_u64(transfer_output.amount)?;
// "secp256k1fx.TransferOutput.OutputOwners" is struct and serialize:"true"
// but embedded inline in the struct "TransferOutput"
// so no need to encode type ID
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/secp256k1fx#TransferOutput
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/secp256k1fx#OutputOwners
packer.pack_u64(transfer_output.output_owners.locktime)?;
packer.pack_u32(transfer_output.output_owners.threshold)?;
packer.pack_u32(transfer_output.output_owners.addresses.len() as u32)?;
for addr in transfer_output.output_owners.addresses.iter() {
packer.pack_bytes(addr.as_ref())?;
}
}
22 => {
// "platformvm::txs::StakeableLockOut"
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#StakeableLockOut
let stakeable_lock_out =
transferable_output.stakeable_lock_out.clone().unwrap();
// marshal "platformvm::txs::StakeableLockOut.locktime" field
packer.pack_u64(stakeable_lock_out.locktime)?;
// "platformvm.StakeableLockOut.TransferOutput" is struct and serialize:"true"
// but embedded inline in the struct "StakeableLockOut"
// so no need to encode type ID
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#StakeableLockOut
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/secp256k1fx#TransferOutput
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/secp256k1fx#OutputOwners
//
// marshal "secp256k1fx.TransferOutput.Amt" field
packer.pack_u64(stakeable_lock_out.transfer_output.amount)?;
packer
.pack_u64(stakeable_lock_out.transfer_output.output_owners.locktime)?;
packer
.pack_u32(stakeable_lock_out.transfer_output.output_owners.threshold)?;
packer.pack_u32(
stakeable_lock_out
.transfer_output
.output_owners
.addresses
.len() as u32,
)?;
for addr in stakeable_lock_out
.transfer_output
.output_owners
.addresses
.iter()
{
packer.pack_bytes(addr.as_ref())?;
}
}
_ => {
return Err(Error::new(
ErrorKind::InvalidInput,
format!(
"unexpected type ID {} for TransferableOutput",
type_id_transferable_out
),
));
}
}
}
} else {
packer.pack_u32(0_u32)?;
}
// take bytes just for hashing computation
let tx_bytes_with_no_signature = packer.take_bytes();
packer.set_bytes(&tx_bytes_with_no_signature);
// compute sha256 for marshaled "unsigned tx" bytes
// IMPORTANT: take the hash only for the type "platformvm.UnsignedExportTx" unsigned tx
// not other fields -- only hash "platformvm.UnsignedExportTx.*" but not "platformvm.Tx.Creds"
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#UnsignedExportTx
let tx_bytes_hash = hash::sha256(&tx_bytes_with_no_signature);
// number of of credentials
let creds_len = signers.len() as u32;
// pack the fourth field in the struct
packer.pack_u32(creds_len)?;
// sign the hash with the signers (in case of multi-sig)
// and combine all signatures into a secp256k1fx credential
self.creds = Vec::new();
for keys in signers.iter() {
let mut sigs: Vec<Vec<u8>> = Vec::new();
for k in keys.iter() {
let sig = k.sign_digest(&tx_bytes_hash).await.map_err(|e| {
Error::new(ErrorKind::Other, format!("failed sign_digest {}", e))
})?;
sigs.push(Vec::from(sig));
}
let mut cred = key::secp256k1::txs::Credential::default();
cred.signatures = sigs;
// add a new credential to "Tx"
self.creds.push(cred);
}
if creds_len > 0 {
// pack each "cred" which is "secp256k1fx.Credential"
// marshal type ID for "secp256k1fx.Credential"
let cred_type_id = key::secp256k1::txs::Credential::type_id();
for cred in self.creds.iter() {
// marshal type ID for "secp256k1fx.Credential"
packer.pack_u32(cred_type_id)?;
// marshal fields for "secp256k1fx.Credential"
packer.pack_u32(cred.signatures.len() as u32)?;
for sig in cred.signatures.iter() {
packer.pack_bytes(sig)?;
}
}
}
let tx_bytes_with_signatures = packer.take_bytes();
let tx_id = hash::sha256(&tx_bytes_with_signatures);
// update "BaseTx.Metadata" with id/unsigned bytes/bytes
// ref. "avalanchego/vms/platformvm.Tx.Sign"
// ref. "avalanchego/vms/components/avax.BaseTx.Metadata.Initialize"
self.base_tx.metadata = Some(txs::Metadata {
id: ids::Id::from_slice(&tx_id),
tx_bytes_with_no_signature: tx_bytes_with_no_signature.to_vec(),
tx_bytes_with_signatures: tx_bytes_with_signatures.to_vec(),
});
Ok(())
}
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
pub async fn sign<T: key::secp256k1::SignOnly>(
&mut self,
signers: Vec<Vec<T>>,
) -> io::Result<()> {
// marshal "unsigned tx" with the codec version
let type_id = Self::type_id();
let packer = self.base_tx.pack(codec::VERSION, type_id)?;
// "avalanchego" marshals the whole struct again for signed bytes
// even when the underlying "unsigned_tx" is already once marshaled
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/avm#Tx.SignSECP256K1Fx
//
// reuse the underlying packer to avoid marshaling the unsigned tx twice
// just marshal the next fields in the struct and pack them all together
// in the existing packer
let b = packer.take_bytes();
packer.set_bytes(&b);
// pack the second field in the struct
packer.pack_bytes(self.destination_chain_id.as_ref())?;
// pack the third field in the struct
if self.destination_chain_transferable_outputs.is_some() {
let destination_chain_transferable_outputs = self
.destination_chain_transferable_outputs
.as_ref()
.unwrap();
packer.pack_u32(destination_chain_transferable_outputs.len() as u32)?;
for transferable_output in destination_chain_transferable_outputs.iter() {
// "TransferableOutput.Asset" is struct and serialize:"true"
// but embedded inline in the struct "TransferableOutput"
// so no need to encode type ID
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/components/avax#TransferableOutput
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/components/avax#Asset
packer.pack_bytes(transferable_output.asset_id.as_ref())?;
// fx_id is serialize:"false" thus skipping serialization
// decide the type
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/components/avax#TransferableOutput
if transferable_output.transfer_output.is_none()
&& transferable_output.stakeable_lock_out.is_none()
{
return Err(Error::new(
ErrorKind::InvalidInput,
"unexpected Nones in TransferableOutput transfer_output and stakeable_lock_out",
));
}
let type_id_transferable_out = {
if transferable_output.transfer_output.is_some() {
key::secp256k1::txs::transfer::Output::type_id()
} else {
platformvm::txs::StakeableLockOut::type_id()
}
};
// marshal type ID for "key::secp256k1::txs::transfer::Output" or "platformvm::txs::StakeableLockOut"
packer.pack_u32(type_id_transferable_out)?;
match type_id_transferable_out {
7 => {
// "key::secp256k1::txs::transfer::Output"
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/secp256k1fx#TransferOutput
let transfer_output = transferable_output.transfer_output.clone().unwrap();
// marshal "secp256k1fx.TransferOutput.Amt" field
packer.pack_u64(transfer_output.amount)?;
// "secp256k1fx.TransferOutput.OutputOwners" is struct and serialize:"true"
// but embedded inline in the struct "TransferOutput"
// so no need to encode type ID
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/secp256k1fx#TransferOutput
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/secp256k1fx#OutputOwners
packer.pack_u64(transfer_output.output_owners.locktime)?;
packer.pack_u32(transfer_output.output_owners.threshold)?;
packer.pack_u32(transfer_output.output_owners.addresses.len() as u32)?;
for addr in transfer_output.output_owners.addresses.iter() {
packer.pack_bytes(addr.as_ref())?;
}
}
22 => {
// "platformvm::txs::StakeableLockOut"
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#StakeableLockOut
let stakeable_lock_out =
transferable_output.stakeable_lock_out.clone().unwrap();
// marshal "platformvm::txs::StakeableLockOut.locktime" field
packer.pack_u64(stakeable_lock_out.locktime)?;
// "platformvm.StakeableLockOut.TransferOutput" is struct and serialize:"true"
// but embedded inline in the struct "StakeableLockOut"
// so no need to encode type ID
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/platformvm#StakeableLockOut
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/secp256k1fx#TransferOutput
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/secp256k1fx#OutputOwners
//
// marshal "secp256k1fx.TransferOutput.Amt" field
packer.pack_u64(stakeable_lock_out.transfer_output.amount)?;
packer
.pack_u64(stakeable_lock_out.transfer_output.output_owners.locktime)?;
packer
.pack_u32(stakeable_lock_out.transfer_output.output_owners.threshold)?;
packer.pack_u32(
stakeable_lock_out
.transfer_output
.output_owners
.addresses
.len() as u32,
)?;
for addr in stakeable_lock_out
.transfer_output
.output_owners
.addresses
.iter()
{
packer.pack_bytes(addr.as_ref())?;
}
}
_ => {
return Err(Error::new(
ErrorKind::InvalidInput,
format!(
"unexpected type ID {} for TransferableOutput",
type_id_transferable_out
),
));
}
}
}
} else {
packer.pack_u32(0_u32)?;
}
// take bytes just for hashing computation
let tx_bytes_with_no_signature = packer.take_bytes();
packer.set_bytes(&tx_bytes_with_no_signature);
// compute sha256 for marshaled "unsigned tx" bytes
// IMPORTANT: take the hash only for the type "avm.ExportTx" unsigned tx
// not other fields -- only hash "avm.ExportTx.*" but not "avm.Tx.Creds"
// ref. https://pkg.go.dev/github.com/ava-labs/avalanchego/vms/avm#ExportTx
let tx_bytes_hash = hash::sha256(&tx_bytes_with_no_signature);
// number of of credentials
let fx_creds_len = signers.len() as u32;
// pack the fourth field in the struct
packer.pack_u32(fx_creds_len)?;
// sign the hash with the signers (in case of multi-sig)
// and combine all signatures into a secp256k1fx credential
self.fx_creds = Vec::new();
for keys in signers.iter() {
let mut sigs: Vec<Vec<u8>> = Vec::new();
for k in keys.iter() {
let sig = k.sign_digest(&tx_bytes_hash).await.map_err(|e| {
Error::new(ErrorKind::Other, format!("failed sign_digest {}", e))
})?;
sigs.push(Vec::from(sig));
}
let mut cred = key::secp256k1::txs::Credential::default();
cred.signatures = sigs;
let mut fx_cred = fx::Credential::default();
fx_cred.cred = cred;
// add a new credential to "Tx"
self.fx_creds.push(fx_cred);
}
if fx_creds_len > 0 {
// pack each "fx_cred" which is "secp256k1fx.Credential"
// marshal type ID for "secp256k1fx.Credential"
let cred_type_id = key::secp256k1::txs::Credential::type_id();
for fx_cred in self.fx_creds.iter() {
packer.pack_u32(cred_type_id)?;
packer.pack_u32(fx_cred.cred.signatures.len() as u32)?;
for sig in fx_cred.cred.signatures.iter() {
packer.pack_bytes(sig)?;
}
}
}
let tx_bytes_with_signatures = packer.take_bytes();
let tx_id = hash::sha256(&tx_bytes_with_signatures);
// update "BaseTx.Metadata" with id/unsigned bytes/bytes
// ref. "avalanchego/vms/avm.Tx.SignSECP256K1Fx"
// ref. "avalanchego/vms/components/avax.BaseTx.Metadata.Initialize"
self.base_tx.metadata = Some(txs::Metadata {
id: ids::Id::from_slice(&tx_id),
tx_bytes_with_no_signature: tx_bytes_with_no_signature.to_vec(),
tx_bytes_with_signatures: tx_bytes_with_signatures.to_vec(),
});
Ok(())
}
Trait Implementations§
source§impl<'de> Deserialize<'de> for Tx
impl<'de> Deserialize<'de> for Tx
source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl Eq for Tx
impl StructuralEq for Tx
impl StructuralPartialEq for Tx
Auto Trait Implementations§
impl RefUnwindSafe for Tx
impl Send for Tx
impl Sync for Tx
impl Unpin for Tx
impl UnwindSafe for Tx
Blanket Implementations§
source§impl<Q, K> Equivalent<K> for Qwhere
Q: Eq + ?Sized,
K: Borrow<Q> + ?Sized,
impl<Q, K> Equivalent<K> for Qwhere
Q: Eq + ?Sized,
K: Borrow<Q> + ?Sized,
source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key
and return true
if they are equal.source§impl<T> Instrument for T
impl<T> Instrument for T
source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T
in a tonic::Request