Skip to content

Commit

Permalink
wallet: combine common event structs, add siafund claim support
Browse files Browse the repository at this point in the history
  • Loading branch information
n8maninger committed Jun 19, 2024
1 parent 83105c6 commit 04b2041
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 60 deletions.
99 changes: 73 additions & 26 deletions wallet/events.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package wallet

import (
"encoding/json"
"fmt"
"time"

"go.sia.tech/core/types"
Expand All @@ -12,40 +14,36 @@ import (
const (
EventTypeMinerPayout = "miner"
EventTypeFoundationSubsidy = "foundation"
EventTypeSiafundClaim = "siafundClaim"

EventTypeV1Transaction = "v1Transaction"
EventTypeV1Contract = "v1Contract"
EventTypeV1Transaction = "v1Transaction"
EventTypeV1ContractResolution = "v1ContractResolution"

EventTypeV2Transaction = "v2Transaction"
EventTypeV2Contract = "v2Contract"
EventTypeV2Transaction = "v2Transaction"
EventTypeV2ContractResolution = "v2ContractResolution"
)

type (
// An EventMinerPayout represents a miner payout from a block.
EventMinerPayout struct {
// An EventPayout represents a miner payout, siafund claim, or foundation
// subsidy.
EventPayout struct {
SiacoinElement types.SiacoinElement `json:"siacoinElement"`
}

// EventFoundationSubsidy represents a foundation subsidy from a block.
EventFoundationSubsidy struct {
SiacoinElement types.SiacoinElement `json:"siacoinElement"`
}

// An EventV1ContractPayout represents a file contract payout from a v1
// An EventV1ContractResolution represents a file contract resolution from a v1
// contract.
EventV1ContractPayout struct {
FileContract types.FileContractElement `json:"fileContract"`
EventV1ContractResolution struct {
Parent types.FileContractElement `json:"parent"`
SiacoinElement types.SiacoinElement `json:"siacoinElement"`
Missed bool `json:"missed"`
}

// An EventV2ContractPayout represents a file contract payout from a v2
// An EventV2ContractResolution represents a file contract payout from a v2
// contract.
EventV2ContractPayout struct {
FileContract types.V2FileContractElement `json:"fileContract"`
Resolution types.V2FileContractResolutionType `json:"resolution"`
SiacoinElement types.SiacoinElement `json:"siacoinElement"`
Missed bool `json:"missed"`
EventV2ContractResolution struct {
types.V2FileContractResolution
SiacoinElement types.SiacoinElement `json:"siacoinElement"`
Missed bool `json:"missed"`
}

// EventV1Transaction is a transaction event that includes the transaction
Expand Down Expand Up @@ -73,9 +71,58 @@ type (
}
)

func (EventMinerPayout) isEvent() bool { return true }
func (EventFoundationSubsidy) isEvent() bool { return true }
func (EventV1ContractPayout) isEvent() bool { return true }
func (EventV2ContractPayout) isEvent() bool { return true }
func (EventV1Transaction) isEvent() bool { return true }
func (EventV2Transaction) isEvent() bool { return true }
func (EventPayout) isEvent() bool { return true }
func (EventV1Transaction) isEvent() bool { return true }
func (EventV1ContractResolution) isEvent() bool { return true }
func (EventV2Transaction) isEvent() bool { return true }
func (EventV2ContractResolution) isEvent() bool { return true }

// UnmarshalJSON implements the json.Unmarshaler interface.
func (e *Event) UnmarshalJSON(b []byte) error {
var je struct {
ID types.Hash256 `json:"id"`
Index types.ChainIndex `json:"index"`
Inflow types.Currency `json:"inflow"`
Outflow types.Currency `json:"outflow"`
Type string `json:"type"`
Data json.RawMessage `json:"data"`
MaturityHeight uint64 `json:"maturityHeight"`
Timestamp time.Time `json:"timestamp"`
}
if err := json.Unmarshal(b, &je); err != nil {
return err
}

e.ID = je.ID
e.Index = je.Index
e.Timestamp = je.Timestamp
e.MaturityHeight = je.MaturityHeight
e.Type = je.Type

var err error
switch je.Type {
case EventTypeMinerPayout, EventTypeFoundationSubsidy, EventTypeSiafundClaim:
var data EventPayout
err = json.Unmarshal(je.Data, &data)
e.Data = data
case EventTypeV1ContractResolution:
var data EventV1ContractResolution
err = json.Unmarshal(je.Data, &data)
e.Data = data
case EventTypeV2ContractResolution:
var data EventV2ContractResolution
err = json.Unmarshal(je.Data, &data)
e.Data = data
case EventTypeV1Transaction:
var data EventV1Transaction
err = json.Unmarshal(je.Data, &data)
e.Data = data
case EventTypeV2Transaction:
var data EventV2Transaction
err = json.Unmarshal(je.Data, &data)
e.Data = data
default:
return fmt.Errorf("unknown event type: %v", je.Type)
}
return err
}
98 changes: 64 additions & 34 deletions wallet/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,29 +54,23 @@ func appliedEvents(cau chain.ApplyUpdate, walletAddress types.Address) (events [
maturityHeight := cs.MaturityHeight()
siacoinElements := make(map[types.SiacoinOutputID]types.SiacoinElement)

// cache the value of spent siacoin elements to use when calculating outflow
// cache the value of siacoin elements to use when calculating v1 outflow
cau.ForEachSiacoinElement(func(se types.SiacoinElement, spent bool) {
if spent {
siacoinElements[types.SiacoinOutputID(se.ID)] = se
}
siacoinElements[types.SiacoinOutputID(se.ID)] = se
})

addEvent := func(id types.Hash256, data EventData) {
addEvent := func(id types.Hash256, eventType string, data EventData) {
ev := Event{
ID: id,
Index: index,
Data: data,
Type: eventType,
Timestamp: block.Timestamp,
}

switch data := data.(type) {
case EventMinerPayout:
ev.Inflow = data.SiacoinElement.SiacoinOutput.Value
ev.Type = EventTypeMinerPayout
ev.MaturityHeight = maturityHeight
case EventFoundationSubsidy:
case EventPayout:
ev.Inflow = data.SiacoinElement.SiacoinOutput.Value
ev.Type = EventTypeFoundationSubsidy
ev.MaturityHeight = maturityHeight
case EventV1Transaction:
for _, si := range data.SiacoinInputs {
Expand All @@ -90,12 +84,10 @@ func appliedEvents(cau chain.ApplyUpdate, walletAddress types.Address) (events [
ev.Inflow = ev.Inflow.Add(so.Value)
}
}
ev.MaturityHeight = index.Height
ev.Type = EventTypeV1Transaction
case EventV1ContractPayout:
ev.MaturityHeight = index.Height // maturity height is the current height
case EventV1ContractResolution:
ev.Inflow = data.SiacoinElement.SiacoinOutput.Value
ev.Type = EventTypeV1Contract
ev.MaturityHeight = cs.MaturityHeight()
ev.MaturityHeight = maturityHeight
case EventV2Transaction:
for _, si := range data.SiacoinInputs {
if si.SatisfiedPolicy.Policy.Address() == walletAddress {
Expand All @@ -108,12 +100,10 @@ func appliedEvents(cau chain.ApplyUpdate, walletAddress types.Address) (events [
ev.Inflow = ev.Inflow.Add(so.Value)
}
}
ev.Type = EventTypeV2Transaction
ev.MaturityHeight = index.Height
case EventV2ContractPayout:
case EventV2ContractResolution:
ev.Inflow = data.SiacoinElement.SiacoinOutput.Value
ev.Type = EventTypeV2Contract
ev.MaturityHeight = cs.MaturityHeight()
ev.MaturityHeight = maturityHeight
}

events = append(events, ev)
Expand All @@ -130,14 +120,32 @@ func appliedEvents(cau chain.ApplyUpdate, walletAddress types.Address) (events [
return true
}
}
for _, si := range txn.SiafundInputs {
if si.ClaimAddress == walletAddress {
return true
}
}
return false
}

for _, txn := range block.Transactions {
if !relevantV1Txn(txn) {
continue
}
addEvent(types.Hash256(txn.ID()), EventV1Transaction(txn))
for _, si := range txn.SiafundInputs {
if si.UnlockConditions.UnlockHash() == walletAddress {
outputID := si.ParentID.ClaimOutputID()
claimOutput, ok := siacoinElements[outputID]
if !ok {
continue
}

addEvent(types.Hash256(outputID), EventTypeSiafundClaim, EventPayout{
SiacoinElement: claimOutput,
})
}
}
addEvent(types.Hash256(txn.ID()), EventTypeV1Transaction, EventV1Transaction(txn))
}

relevantV2Txn := func(txn types.V2Transaction) bool {
Expand All @@ -151,14 +159,32 @@ func appliedEvents(cau chain.ApplyUpdate, walletAddress types.Address) (events [
return true
}
}
for _, si := range txn.SiafundInputs {
if si.ClaimAddress == walletAddress {
return true
}
}
return false
}

for _, txn := range block.V2Transactions() {
if !relevantV2Txn(txn) {
continue
}
addEvent(types.Hash256(txn.ID()), EventV2Transaction(txn))
for _, si := range txn.SiafundInputs {
if si.Parent.SiafundOutput.Address == walletAddress {
outputID := types.SiafundOutputID(si.Parent.ID).V2ClaimOutputID()
claimOutput, ok := siacoinElements[outputID]
if !ok {
continue
}

addEvent(types.Hash256(outputID), EventTypeSiafundClaim, EventPayout{
SiacoinElement: claimOutput,
})
}
}
addEvent(types.Hash256(txn.ID()), EventTypeV2Transaction, EventV2Transaction(txn))
}

cau.ForEachFileContractElement(func(fce types.FileContractElement, rev *types.FileContractElement, resolved bool, valid bool) {
Expand All @@ -173,8 +199,8 @@ func appliedEvents(cau chain.ApplyUpdate, walletAddress types.Address) (events [
}

outputID := types.FileContractID(fce.ID).ValidOutputID(i)
addEvent(types.Hash256(types.FileContractID(fce.ID).ValidOutputID(0)), EventV1ContractPayout{
FileContract: fce,
addEvent(types.Hash256(types.FileContractID(fce.ID).ValidOutputID(0)), EventTypeV1ContractResolution, EventV1ContractResolution{
Parent: fce,
SiacoinElement: siacoinElements[outputID],
Missed: false,
})
Expand All @@ -186,8 +212,8 @@ func appliedEvents(cau chain.ApplyUpdate, walletAddress types.Address) (events [
}

outputID := types.FileContractID(fce.ID).MissedOutputID(i)
addEvent(types.Hash256(types.FileContractID(fce.ID).MissedOutputID(0)), EventV1ContractPayout{
FileContract: fce,
addEvent(types.Hash256(types.FileContractID(fce.ID).MissedOutputID(0)), EventTypeV1ContractResolution, EventV1ContractResolution{
Parent: fce,
SiacoinElement: siacoinElements[outputID],
Missed: true,
})
Expand All @@ -207,19 +233,23 @@ func appliedEvents(cau chain.ApplyUpdate, walletAddress types.Address) (events [

if fce.V2FileContract.HostOutput.Address == walletAddress {
outputID := types.FileContractID(fce.ID).V2HostOutputID()
addEvent(types.Hash256(outputID), EventV2ContractPayout{
FileContract: fce,
Resolution: res,
addEvent(types.Hash256(outputID), EventTypeV2ContractResolution, EventV2ContractResolution{
V2FileContractResolution: types.V2FileContractResolution{
Parent: fce,
Resolution: res,
},
SiacoinElement: siacoinElements[outputID],
Missed: missed,
})
}

if fce.V2FileContract.RenterOutput.Address == walletAddress {
outputID := types.FileContractID(fce.ID).V2RenterOutputID()
addEvent(types.Hash256(outputID), EventV2ContractPayout{
FileContract: fce,
Resolution: res,
addEvent(types.Hash256(outputID), EventTypeV2ContractResolution, EventV2ContractResolution{
V2FileContractResolution: types.V2FileContractResolution{
Parent: fce,
Resolution: res,
},
SiacoinElement: siacoinElements[outputID],
Missed: missed,
})
Expand All @@ -233,7 +263,7 @@ func appliedEvents(cau chain.ApplyUpdate, walletAddress types.Address) (events [
}

outputID := blockID.MinerOutputID(i)
addEvent(types.Hash256(outputID), EventMinerPayout{
addEvent(types.Hash256(outputID), EventTypeMinerPayout, EventPayout{
SiacoinElement: siacoinElements[outputID],
})
}
Expand All @@ -243,7 +273,7 @@ func appliedEvents(cau chain.ApplyUpdate, walletAddress types.Address) (events [
if !ok || se.SiacoinOutput.Address != walletAddress {
return
}
addEvent(types.Hash256(outputID), EventFoundationSubsidy{
addEvent(types.Hash256(outputID), EventTypeFoundationSubsidy, EventPayout{
SiacoinElement: se,
})

Expand Down

0 comments on commit 04b2041

Please sign in to comment.