Skip to content

Commit f943ed9

Browse files
committed
Added plumbing for coin spends
1 parent b1979e8 commit f943ed9

File tree

2 files changed

+137
-17
lines changed

2 files changed

+137
-17
lines changed

src/common/types.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl ToClvm<NodePtr> for CoinID {
5050
}
5151

5252
/// Coin String
53-
#[derive(Default, Clone)]
53+
#[derive(Default, Clone, Debug)]
5454
pub struct CoinString(Vec<u8>);
5555

5656
impl CoinString {
@@ -235,6 +235,10 @@ impl Aggsig {
235235
Aggsig::from_bytes(fixed)
236236
}
237237

238+
pub fn bytes(&self) -> [u8; 96] {
239+
self.0.to_bytes()
240+
}
241+
238242
pub fn to_bls(&self) -> chia_bls::Signature {
239243
self.0.clone()
240244
}

src/tests/simulator.rs

+132-16
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use indoc::indoc;
1111

1212
use crate::common::constants::CREATE_COIN;
1313
use crate::common::standard_coin::standard_solution;
14-
use crate::common::types::{ErrToError, Error, Puzzle, Amount, Hash, CoinString, CoinID, PuzzleHash, PrivateKey, Aggsig, Node, SpecificTransactionBundle, AllocEncoder};
14+
use crate::common::types::{ErrToError, Error, Puzzle, Amount, Hash, CoinString, CoinID, PuzzleHash, PrivateKey, Aggsig, Node, SpecificTransactionBundle, AllocEncoder, TransactionBundle};
1515
use crate::common::standard_coin::ChiaIdentity;
1616

1717
// Allow simulator from rust.
@@ -23,6 +23,9 @@ struct Simulator {
2323
make_spend: PyObject,
2424
chia_rs_coin: PyObject,
2525
program: PyObject,
26+
spend_bundle: PyObject,
27+
g2_element: PyObject,
28+
coin_as_list: PyObject
2629
}
2730

2831
#[cfg(test)]
@@ -53,14 +56,16 @@ impl Simulator {
5356
import asyncio
5457
import chia.clvm.spend_sim
5558
from chia.types.coin_spend import make_spend
56-
from chia_rs import Coin
59+
from chia_rs import Coin, G2Element
5760
from chia.types.blockchain_format.program import Program
61+
from chia.types.spend_bundle import SpendBundle
62+
from chia.types.blockchain_format.coin import coin_as_list
5863
5964
def start():
6065
evloop = asyncio.new_event_loop()
6166
sac_gen = chia.clvm.spend_sim.sim_and_client()
6267
(sim, client) = evloop.run_until_complete(sac_gen.__aenter__())
63-
return (evloop, sim, client, sac_gen, make_spend, Coin, Program)
68+
return (evloop, sim, client, sac_gen, make_spend, Coin, Program, SpendBundle, G2Element, coin_as_list)
6469
"},
6570
"tmod.py",
6671
"tmod",
@@ -74,6 +79,9 @@ impl Simulator {
7479
make_spend: evloop.get_item(4)?.extract()?,
7580
chia_rs_coin: evloop.get_item(5)?.extract()?,
7681
program: evloop.get_item(6)?.extract()?,
82+
spend_bundle: evloop.get_item(7)?.extract()?,
83+
g2_element: evloop.get_item(8)?.extract()?,
84+
coin_as_list: evloop.get_item(9)?.extract()?,
7785
})
7886
})
7987
.expect("should work")
@@ -88,17 +96,60 @@ impl Simulator {
8896
Ok(res.into())
8997
}
9098

91-
pub fn farm_block(&self, private_key: &PrivateKey) {
99+
fn async_client<ArgT>(&self, py: Python<'_>, name: &str, args: ArgT) -> PyResult<PyObject>
100+
where
101+
ArgT: IntoPy<Py<PyTuple>>
102+
{
103+
let task = self.client.call_method1(py, name, args)?;
104+
let res = self.evloop.call_method1(py, "run_until_complete", (task,))?;
105+
Ok(res.into())
106+
}
107+
108+
pub fn farm_block(&self, puzzle_hash: &PuzzleHash) {
92109
Python::with_gil(|py| -> PyResult<_> {
93-
let private_key_bytes = PyBytes::new(py, &private_key.bytes());
94-
self.async_call(py, "farm_block", (private_key_bytes,))?;
110+
let puzzle_hash_bytes = PyBytes::new(py, &puzzle_hash.bytes());
111+
self.async_call(py, "farm_block", (puzzle_hash_bytes,))?;
95112
Ok(())
96113
})
97114
.expect("should farm");
98115
}
99116

100-
pub fn get_my_coins(&self) -> PyResult<Vec<CoinString>> {
101-
todo!();
117+
pub fn get_my_coins(&self, puzzle_hash: &PuzzleHash) -> PyResult<Vec<CoinString>> {
118+
Python::with_gil(|py| -> PyResult<_> {
119+
let hash_bytes = PyBytes::new(py, &puzzle_hash.bytes());
120+
let coins = self.async_client(
121+
py,
122+
"get_coin_records_by_puzzle_hash",
123+
(hash_bytes, false)
124+
)?;
125+
let items: Vec<PyObject> = coins.extract(py)?;
126+
eprintln!("num coins {}", items.len());
127+
let mut result_coins = Vec::new();
128+
for i in items.iter() {
129+
let coin_of_item: PyObject = i.getattr(py, "coin")?.extract(py)?;
130+
let as_list: Vec<PyObject> = self.coin_as_list.call1(py, (coin_of_item,))?.extract(py)?;
131+
let parent_coin_info: &PyBytes = as_list[0].downcast(py)?;
132+
let parent_coin_info_slice: &[u8] = parent_coin_info.extract()?;
133+
let puzzle_hash: &PyBytes = as_list[1].downcast(py)?;
134+
let puzzle_hash_slice: &[u8] = puzzle_hash.extract()?;
135+
let amount: u64 = as_list[2].extract(py)?;
136+
let parent_coin_hash = Hash::from_slice(parent_coin_info_slice);
137+
let puzzle_hash = Hash::from_slice(puzzle_hash_slice);
138+
result_coins.push(CoinString::from_parts(
139+
&CoinID::new(parent_coin_hash),
140+
&PuzzleHash::from_hash(puzzle_hash),
141+
&Amount::new(amount)
142+
));
143+
}
144+
Ok(result_coins)
145+
})
146+
}
147+
148+
pub fn g2_element(&self, aggsig: &Aggsig) -> PyResult<PyObject> {
149+
Python::with_gil(|py| -> PyResult<_> {
150+
let bytes = PyBytes::new(py, &aggsig.bytes());
151+
self.g2_element.call_method1(py, "from_bytes_unchecked", (bytes,))
152+
})
102153
}
103154

104155
pub fn make_coin(&self, coin_string: &CoinString) -> PyResult<PyObject> {
@@ -145,8 +196,43 @@ impl Simulator {
145196
self.make_spend.call1(py, (coin, puzzle_program, solution_program))
146197
}
147198

148-
pub fn perform_spend(&self, tx: &[SpecificTransactionBundle]) -> PyResult<()> {
149-
todo!();
199+
pub fn make_spend_bundle(
200+
&self,
201+
allocator: &mut AllocEncoder,
202+
tx: &SpecificTransactionBundle
203+
) -> PyResult<PyObject> {
204+
Python::with_gil(|py| {
205+
let spend = self.make_coin_spend(
206+
py,
207+
allocator,
208+
&tx.coin,
209+
tx.bundle.puzzle.clone(),
210+
tx.bundle.solution
211+
)?;
212+
let signature = self.g2_element(&tx.bundle.signature)?;
213+
self.spend_bundle.call1(py, ([spend], signature))
214+
})
215+
}
216+
217+
pub fn push_tx(
218+
&self,
219+
allocator: &mut AllocEncoder,
220+
tx: &SpecificTransactionBundle
221+
) -> PyResult<u32> {
222+
let spend_bundle = self.make_spend_bundle(allocator, tx)?;
223+
Python::with_gil(|py| {
224+
eprintln!("spend_bundle {:?}", spend_bundle);
225+
let spend_res: PyObject = self.async_client(
226+
py,
227+
"push_tx",
228+
(spend_bundle,)
229+
)?.extract(py)?;
230+
eprintln!("spend_res {:?}", spend_res);
231+
let (inclusion_status, err): (PyObject, PyObject) = spend_res.extract(py)?;
232+
eprintln!("inclusion_status {inclusion_status}");
233+
let status: u32 = inclusion_status.extract(py)?;
234+
Ok(status)
235+
})
150236
}
151237
}
152238

@@ -157,21 +243,35 @@ fn test_sim() {
157243
let mut allocator = AllocEncoder::new();
158244
let s = Simulator::new();
159245
let private_key: PrivateKey = rng.gen();
160-
s.farm_block(&private_key);
161246
let identity = ChiaIdentity::new(&mut allocator, private_key.clone()).expect("should create");
162-
let conditions = (
247+
s.farm_block(&identity.puzzle_hash);
248+
249+
let coins = s.get_my_coins(&identity.puzzle_hash).expect("got coins");
250+
eprintln!("coins {coins:?}");
251+
252+
let (first_coin_parent, first_coin_ph, first_coin_amt) = coins[0].to_parts().unwrap();
253+
assert_eq!(first_coin_ph, identity.puzzle_hash);
254+
255+
let conditions = [
163256
(CREATE_COIN,
164257
(identity.puzzle_hash.clone(),
165258
(Amount::new(1), ())
166259
)
167-
), ()
168-
).to_clvm(&mut allocator).expect("should create conditions");
260+
),
261+
(CREATE_COIN,
262+
(identity.puzzle_hash.clone(),
263+
(first_coin_amt - Amount::new(1), ())
264+
)
265+
)
266+
].to_clvm(&mut allocator).expect("should create conditions");
267+
169268
let (solution, signature) = standard_solution(
170269
&mut allocator,
171270
&private_key,
172271
conditions
173272
).expect("should build");
174-
let s =
273+
274+
let spend =
175275
Python::with_gil(|py| {
176276
s.make_coin_spend(
177277
py,
@@ -181,5 +281,21 @@ fn test_sim() {
181281
solution
182282
)
183283
}).expect("should get a spend");
184-
eprintln!("spend {s:?}");
284+
285+
eprintln!("spend {spend:?}");
286+
287+
let specific = SpecificTransactionBundle {
288+
coin: coins[0].clone(),
289+
bundle: TransactionBundle {
290+
puzzle: identity.puzzle.clone(),
291+
solution,
292+
signature,
293+
}
294+
};
295+
296+
let status = s.push_tx(&mut allocator, &specific).expect("should spend");
297+
298+
// Do at end
299+
drop(s);
300+
assert_ne!(status, 3);
185301
}

0 commit comments

Comments
 (0)