@@ -208,6 +208,21 @@ func (n *testChain) assertSCE(t *testing.T, scID types.SiacoinOutputID, index *t
208208 testutil .Equal (t , "sce.SiacoinElement.SiacoinOutput" , sco , sce .SiacoinOutput )
209209}
210210
211+ // helper to assert the Siafund element in the db has the right source, index and output
212+ func (n * testChain ) assertSFE (t * testing.T , sfID types.SiafundOutputID , index * types.ChainIndex , sfo types.SiafundOutput ) {
213+ t .Helper ()
214+
215+ sfes , err := n .db .SiafundElements ([]types.SiafundOutputID {sfID })
216+ if err != nil {
217+ t .Fatal (err )
218+ }
219+ testutil .Equal (t , "len(sfes)" , 1 , len (sfes ))
220+
221+ sfe := sfes [0 ]
222+ testutil .Equal (t , "sfe.SpentIndex" , index , sfe .SpentIndex )
223+ testutil .Equal (t , "sfe.SiafundElement.SiafundOutput" , sfo , sfe .SiafundOutput )
224+ }
225+
211226func TestSiacoinOutput (t * testing.T ) {
212227 pk1 := types .GeneratePrivateKey ()
213228 uc1 := types .StandardUnlockConditions (pk1 .PublicKey ())
@@ -332,6 +347,130 @@ func TestEphemeralSiacoinOutput(t *testing.T) {
332347 }
333348}
334349
350+ func TestSiafundOutput (t * testing.T ) {
351+ pk1 := types .GeneratePrivateKey ()
352+ uc1 := types .StandardUnlockConditions (pk1 .PublicKey ())
353+ addr1 := uc1 .UnlockHash ()
354+
355+ pk2 := types .GeneratePrivateKey ()
356+ uc2 := types .StandardUnlockConditions (pk2 .PublicKey ())
357+ addr2 := uc2 .UnlockHash ()
358+
359+ n := newTestChain (t , false , func (network * consensus.Network , genesisBlock types.Block ) {
360+ genesisBlock .Transactions [0 ].SiafundOutputs [0 ].Address = addr1
361+ })
362+ sfID := n .genesis ().Transactions [0 ].SiafundOutputID (0 )
363+
364+ // genesis output should be unspent
365+ // so spentIndex = nil
366+ n .assertSFE (t , sfID , nil , n .genesis ().Transactions [0 ].SiafundOutputs [0 ])
367+
368+ txn1 := types.Transaction {
369+ SiafundInputs : []types.SiafundInput {{
370+ ParentID : sfID ,
371+ UnlockConditions : uc1 ,
372+ }},
373+ SiafundOutputs : []types.SiafundOutput {{
374+ Address : addr2 ,
375+ Value : n .genesis ().Transactions [0 ].SiafundOutputs [0 ].Value ,
376+ }},
377+ }
378+ testutil .SignTransaction (n .tipState (), pk1 , & txn1 )
379+
380+ n .mineTransactions (t , txn1 )
381+
382+ // genesis output should be spent
383+ tip := n .tipState ().Index
384+ n .assertSFE (t , sfID , & tip , n .genesis ().Transactions [0 ].SiafundOutputs [0 ])
385+
386+ // the output from txn1 should exist now that the block with txn1 was
387+ // mined
388+ n .assertSFE (t , txn1 .SiafundOutputID (0 ), nil , txn1 .SiafundOutputs [0 ])
389+
390+ n .revertBlock (t )
391+
392+ // the genesis output should be unspent now because we reverted the block
393+ // containing txn1 which spent it
394+ n .assertSFE (t , sfID , nil , n .genesis ().Transactions [0 ].SiafundOutputs [0 ])
395+
396+ // the output from txn1 should not exist after txn1 reverted
397+ {
398+ sfes , err := n .db .SiafundElements ([]types.SiafundOutputID {txn1 .SiafundOutputID (0 )})
399+ if err != nil {
400+ t .Fatal (err )
401+ }
402+ testutil .Equal (t , "len(sfes)" , 0 , len (sfes ))
403+ }
404+ }
405+
406+ func TestEphemeralSiafundOutput (t * testing.T ) {
407+ pk1 := types .GeneratePrivateKey ()
408+ uc1 := types .StandardUnlockConditions (pk1 .PublicKey ())
409+ addr1 := uc1 .UnlockHash ()
410+
411+ pk2 := types .GeneratePrivateKey ()
412+ uc2 := types .StandardUnlockConditions (pk2 .PublicKey ())
413+ addr2 := uc2 .UnlockHash ()
414+
415+ n := newTestChain (t , false , func (network * consensus.Network , genesisBlock types.Block ) {
416+ genesisBlock .Transactions [0 ].SiafundOutputs [0 ].Address = addr1
417+ })
418+ sfID := n .genesis ().Transactions [0 ].SiafundOutputID (0 )
419+
420+ // genesis output should be unspent
421+ // so spentIndex = nil
422+ n .assertSFE (t , sfID , nil , n .genesis ().Transactions [0 ].SiafundOutputs [0 ])
423+
424+ txn1 := types.Transaction {
425+ SiafundInputs : []types.SiafundInput {{
426+ ParentID : sfID ,
427+ UnlockConditions : uc1 ,
428+ }},
429+ SiafundOutputs : []types.SiafundOutput {{
430+ Address : addr2 ,
431+ Value : n .genesis ().Transactions [0 ].SiafundOutputs [0 ].Value ,
432+ }},
433+ }
434+ testutil .SignTransaction (n .tipState (), pk1 , & txn1 )
435+
436+ txn2 := types.Transaction {
437+ SiafundInputs : []types.SiafundInput {{
438+ ParentID : txn1 .SiafundOutputID (0 ),
439+ UnlockConditions : uc2 ,
440+ }},
441+ SiafundOutputs : []types.SiafundOutput {{
442+ Address : types .VoidAddress ,
443+ Value : txn1 .SiafundOutputs [0 ].Value ,
444+ }},
445+ }
446+ testutil .SignTransaction (n .tipState (), pk2 , & txn2 )
447+
448+ n .mineTransactions (t , txn1 , txn2 )
449+
450+ tip := n .tipState ().Index
451+ // genesis output should be spent
452+ n .assertSFE (t , sfID , & tip , n .genesis ().Transactions [0 ].SiafundOutputs [0 ])
453+
454+ // now that txn1 and txn2 are mined the outputs from them should exist
455+ n .assertSFE (t , txn1 .SiafundOutputID (0 ), & tip , txn1 .SiafundOutputs [0 ])
456+ n .assertSFE (t , txn2 .SiafundOutputID (0 ), nil , txn2 .SiafundOutputs [0 ])
457+
458+ n .revertBlock (t )
459+
460+ // genesis output should be unspent now that we reverted
461+ n .assertSFE (t , sfID , nil , n .genesis ().Transactions [0 ].SiafundOutputs [0 ])
462+
463+ // outputs from txn1 and txn2 should not exist because those transactions
464+ // were reverted
465+ {
466+ sfes , err := n .db .SiafundElements ([]types.SiafundOutputID {txn1 .SiafundOutputID (0 ), txn2 .SiafundOutputID (0 )})
467+ if err != nil {
468+ t .Fatal (err )
469+ }
470+ testutil .Equal (t , "len(sfes)" , 0 , len (sfes ))
471+ }
472+ }
473+
335474func TestTransactionChainIndices (t * testing.T ) {
336475 n := newTestChain (t , false , nil )
337476
0 commit comments