Skip to content

Commit 7dc2d27

Browse files
authored
Merge pull request #3 from iskyd/main
build unsigned stx token transfer transaction
2 parents dbaf4d1 + 363cbdf commit 7dc2d27

File tree

5 files changed

+144
-28
lines changed

5 files changed

+144
-28
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
# Stacks Development Toolkit (SDK)
1+
# Stacks Development Kit (SDK)

src/network/mod.rs

+8
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ impl NetworkKind {
1313
NetworkKind::Mocknet => 0x80000000,
1414
}
1515
}
16+
17+
fn version_number(&self) -> u8 {
18+
match *self {
19+
NetworkKind::Mainnet => 0b00000000,
20+
NetworkKind::Testnet => 0b10000000,
21+
NetworkKind::Mocknet => 0b10000000,
22+
}
23+
}
1624
}
1725

1826
#[derive(Clone)]

src/transactions/authorization.rs

+49-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
use crate::transactions::constants::PubKeyEncoding;
21
use stacks_common::address::AddressHashMode;
2+
use stacks_common::util::secp256k1::Secp256k1PublicKey;
33
use stacks_common::util::uint::Uint256;
44

5+
#[derive(Debug, PartialEq, Eq)]
56
pub enum SingleSigHashMode {
67
P2PKH,
78
P2WPKH,
@@ -17,10 +18,51 @@ impl SingleSigHashMode {
1718
}
1819

1920
pub struct SingleSigSpendingCondition {
20-
hash_mode: SingleSigHashMode,
21-
signer: String,
22-
nonce: Uint256,
23-
fee: Uint256,
24-
pubkey_encoding: PubKeyEncoding,
25-
signature: Vec<u8>,
21+
pub hash_mode: SingleSigHashMode,
22+
pub nonce: Uint256,
23+
pub fee: Uint256,
24+
pub sender_pubkey: Secp256k1PublicKey,
25+
pub signature: Option<Vec<u8>>,
26+
}
27+
28+
impl SingleSigSpendingCondition {
29+
pub fn new(
30+
hash_mode: SingleSigHashMode,
31+
nonce: Uint256,
32+
fee: Uint256,
33+
sender_pubkey: Secp256k1PublicKey,
34+
signature: Option<Vec<u8>>,
35+
) -> SingleSigSpendingCondition {
36+
SingleSigSpendingCondition {
37+
hash_mode,
38+
nonce,
39+
fee,
40+
sender_pubkey,
41+
signature,
42+
}
43+
}
44+
}
45+
46+
pub struct MultiSigSpendingCondition {}
47+
48+
pub enum SpendingCondition {
49+
SingleSig(SingleSigSpendingCondition),
50+
MultiSig(MultiSigSpendingCondition),
51+
}
52+
53+
pub struct StandardAuthorization {
54+
pub spending_condition: SpendingCondition,
55+
}
56+
57+
impl StandardAuthorization {
58+
pub fn new(spending_condition: SpendingCondition) -> StandardAuthorization {
59+
StandardAuthorization { spending_condition }
60+
}
61+
}
62+
63+
pub struct SponsoredAuthorization {}
64+
65+
pub enum Authorization {
66+
Standard(StandardAuthorization),
67+
Sponsored(SponsoredAuthorization),
2668
}

src/transactions/constants.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ impl PostConditionMode {
5151
}
5252

5353
pub enum AnchorMode {
54-
OnChainOnly,
55-
OffChainOnly,
56-
Any,
54+
OnChainOnly, // The transaction MUST be included in an anchored block
55+
OffChainOnly, // The transaction MUST be included in a microblock
56+
Any, // The leader can choose where to include the transaction.
5757
}
5858

5959
impl AnchorMode {

src/transactions/tx.rs

+83-17
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::network::NetworkKind;
2+
use crate::transactions::authorization::*;
23
use crate::transactions::clarity::ClarityType;
34
use crate::transactions::constants::*;
45
use stacks_common::address::c32::c32_address;
@@ -9,6 +10,7 @@ use stacks_common::types::chainstate::StacksAddress;
910
use stacks_common::util::hash::Hash160;
1011
use stacks_common::util::secp256k1::Secp256k1PrivateKey;
1112
use stacks_common::util::secp256k1::Secp256k1PublicKey;
13+
use stacks_common::util::uint::Uint256;
1214
use std::fmt;
1315

1416
#[derive(Debug)]
@@ -39,9 +41,9 @@ pub trait Serialize {
3941
}
4042

4143
pub struct TokenTransferPayload {
42-
recipient: String,
43-
amount: u64,
44-
memo: String,
44+
pub recipient: String,
45+
pub amount: u64,
46+
pub memo: String,
4547
}
4648

4749
impl Serialize for TokenTransferPayload {
@@ -91,48 +93,66 @@ impl Serialize for TokenTransferPayload {
9193

9294
TokenTransferPayload {
9395
recipient: addr,
94-
amount: amount,
96+
amount,
9597
memo: String::from(memo.trim_matches(char::from(0))),
9698
}
9799
}
98100
}
99101

100-
enum Payload {
102+
pub enum Payload {
101103
TokenTransfer(TokenTransferPayload),
102104
}
103105

104106
pub struct StacksTransaction {
105-
version: TransactionVersion,
106-
network: NetworkKind,
107-
payload: Payload,
108-
post_condition_mode: PostConditionMode,
107+
pub version: TransactionVersion,
108+
pub network: NetworkKind,
109+
pub payload: Payload,
110+
pub post_condition_mode: PostConditionMode,
109111
// post_conditions:
110-
anchor_mode: AnchorMode,
112+
pub anchor_mode: AnchorMode,
113+
pub authorization: Authorization,
111114
}
112115

113-
pub fn build_token_transfer_transaction(
116+
pub fn build_single_sig_stx_token_transfer_transaction(
114117
recipient: String,
115118
amount: u64,
116119
sender_key: Secp256k1PrivateKey, // private key
117120
network: NetworkKind,
118121
memo: String,
119-
nonce: Option<u64>,
120-
fee: Option<u64>,
122+
nonce: Option<Uint256>,
123+
fee: Option<Uint256>,
121124
) -> StacksTransaction {
122125
let public_key = Secp256k1PublicKey::from_private(&sender_key);
123126
let addr =
124127
StacksAddress::from_public_keys(1, &AddressHashMode::SerializeP2WPKH, 1, &vec![public_key])
125128
.expect("Invalid params for generating address");
126129

130+
let single_sig_spending_condition = SingleSigSpendingCondition::new(
131+
SingleSigHashMode::P2WPKH,
132+
match nonce {
133+
Some(n) => n,
134+
None => Uint256::from_u64(0),
135+
},
136+
match fee {
137+
Some(n) => n,
138+
None => Uint256::from_u64(0),
139+
},
140+
public_key,
141+
None,
142+
);
143+
let authorization =
144+
StandardAuthorization::new(SpendingCondition::SingleSig(single_sig_spending_condition));
145+
127146
StacksTransaction {
128147
payload: Payload::TokenTransfer(TokenTransferPayload {
129-
amount: amount,
130-
memo: memo,
131-
recipient: recipient,
148+
amount,
149+
memo,
150+
recipient,
132151
}),
133152
network: network.clone(),
134-
post_condition_mode: PostConditionMode::Deny,
153+
post_condition_mode: PostConditionMode::Deny, // Token transfer cannot have post conditions
135154
version: TransactionVersion::from_network(&network),
155+
authorization: Authorization::Standard(authorization),
136156
anchor_mode: AnchorMode::Any,
137157
}
138158
}
@@ -232,4 +252,50 @@ mod tests {
232252
);
233253
assert_eq!(payload.memo, String::from(""));
234254
}
255+
256+
#[test]
257+
fn build_usingned_single_sig_tx() {
258+
let sender_key = Secp256k1PrivateKey::from_seed(&[2; 32]);
259+
let unsigned_token_transfer_tx = build_single_sig_stx_token_transfer_transaction(
260+
String::from("SP3FGQ8Z7JY9BWYZ5WM53E0M9NK7WHJF0691NZ159"),
261+
10000,
262+
sender_key,
263+
NetworkKind::Mainnet,
264+
String::from("test memo"),
265+
None,
266+
None,
267+
);
268+
269+
match unsigned_token_transfer_tx.authorization {
270+
Authorization::Standard(a) => match a.spending_condition {
271+
SpendingCondition::SingleSig(s) => {
272+
assert_eq!(s.nonce, Uint256::from_u64(0));
273+
assert_eq!(s.fee, Uint256::from_u64(0));
274+
assert_eq!(s.hash_mode, SingleSigHashMode::P2WPKH);
275+
assert_eq!(s.signature, None);
276+
assert_eq!(
277+
s.sender_pubkey,
278+
Secp256k1PublicKey::from_private(&sender_key)
279+
);
280+
}
281+
SpendingCondition::MultiSig(_) => {
282+
assert_eq!(true, false);
283+
}
284+
},
285+
Authorization::Sponsored(_) => {
286+
assert_eq!(true, false);
287+
}
288+
}
289+
290+
match unsigned_token_transfer_tx.payload {
291+
Payload::TokenTransfer(p) => {
292+
assert_eq!(p.amount, 10000);
293+
assert_eq!(p.memo, String::from("test memo"));
294+
assert_eq!(
295+
p.recipient,
296+
String::from("SP3FGQ8Z7JY9BWYZ5WM53E0M9NK7WHJF0691NZ159")
297+
);
298+
}
299+
}
300+
}
235301
}

0 commit comments

Comments
 (0)