Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

consensus: Rename SiafundPool -> SiafundTaxRevenue #235

Merged
merged 4 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 16 additions & 21 deletions consensus/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,11 @@ func (n *Network) GenesisState() State {
return State{
Network: n,

Index: types.ChainIndex{Height: ^uint64(0)},
PrevTimestamps: [11]time.Time{},
Depth: intToTarget(maxTarget),
ChildTarget: n.InitialTarget,
SiafundPool: types.ZeroCurrency,
Index: types.ChainIndex{Height: ^uint64(0)},
PrevTimestamps: [11]time.Time{},
Depth: intToTarget(maxTarget),
ChildTarget: n.InitialTarget,
SiafundTaxRevenue: types.ZeroCurrency,

OakTime: 0,
OakTarget: intToTarget(maxTarget),
Expand All @@ -111,11 +111,11 @@ func (n *Network) GenesisState() State {
type State struct {
Network *Network `json:"-"` // network parameters are not encoded

Index types.ChainIndex `json:"index"`
PrevTimestamps [11]time.Time `json:"prevTimestamps"` // newest -> oldest
Depth types.BlockID `json:"depth"`
ChildTarget types.BlockID `json:"childTarget"`
SiafundPool types.Currency `json:"siafundPool"`
Index types.ChainIndex `json:"index"`
PrevTimestamps [11]time.Time `json:"prevTimestamps"` // newest -> oldest
Depth types.BlockID `json:"depth"`
ChildTarget types.BlockID `json:"childTarget"`
SiafundTaxRevenue types.Currency `json:"siafundTaxRevenue"`

// Oak hardfork state
OakTime time.Duration `json:"oakTime"`
Expand All @@ -139,7 +139,7 @@ func (s State) EncodeTo(e *types.Encoder) {
}
s.Depth.EncodeTo(e)
s.ChildTarget.EncodeTo(e)
types.V2Currency(s.SiafundPool).EncodeTo(e)
types.V2Currency(s.SiafundTaxRevenue).EncodeTo(e)

e.WriteUint64(uint64(s.OakTime))
s.OakTarget.EncodeTo(e)
Expand All @@ -160,7 +160,7 @@ func (s *State) DecodeFrom(d *types.Decoder) {
}
s.Depth.DecodeFrom(d)
s.ChildTarget.DecodeFrom(d)
(*types.V2Currency)(&s.SiafundPool).DecodeFrom(d)
(*types.V2Currency)(&s.SiafundTaxRevenue).DecodeFrom(d)

s.OakTime = time.Duration(d.ReadUint64())
s.OakTarget.DecodeFrom(d)
Expand Down Expand Up @@ -370,12 +370,7 @@ func (s State) FileContractTax(fc types.FileContract) types.Currency {

// V2FileContractTax computes the tax levied on a given v2 contract.
func (s State) V2FileContractTax(fc types.V2FileContract) types.Currency {
sum := fc.RenterOutput.Value.Add(fc.HostOutput.Value)
tax := sum.Div64(25) // 4%
// round down to nearest multiple of SiafundCount
_, r := bits.Div64(0, tax.Hi, s.SiafundCount())
_, r = bits.Div64(r, tax.Lo, s.SiafundCount())
return tax.Sub(types.NewCurrency64(r))
return fc.RenterOutput.Value.Add(fc.HostOutput.Value).Div64(25) // 4%
}

// StorageProofLeafIndex returns the leaf index used when computing or
Expand Down Expand Up @@ -533,7 +528,7 @@ func (s State) PartialSigHash(txn types.Transaction, cf types.CoveredFields) typ

// TransactionsCommitment returns the commitment hash covering the transactions
// that comprise a child block.
func (s *State) TransactionsCommitment(txns []types.Transaction, v2txns []types.V2Transaction) types.Hash256 {
func (s State) TransactionsCommitment(txns []types.Transaction, v2txns []types.V2Transaction) types.Hash256 {
var acc blake2b.Accumulator
for _, txn := range txns {
acc.AddLeaf(txn.FullHash())
Expand Down Expand Up @@ -595,7 +590,7 @@ type MidState struct {
res map[types.FileContractID]bool
v2revs map[types.FileContractID]*types.V2FileContractElement
v2res map[types.FileContractID]types.V2FileContractResolutionType
siafundPool types.Currency
siafundTaxRevenue types.Currency
foundationPrimary types.Address
foundationFailsafe types.Address

Expand Down Expand Up @@ -657,7 +652,7 @@ func NewMidState(s State) *MidState {
res: make(map[types.FileContractID]bool),
v2revs: make(map[types.FileContractID]*types.V2FileContractElement),
v2res: make(map[types.FileContractID]types.V2FileContractResolutionType),
siafundPool: s.SiafundPool,
siafundTaxRevenue: s.SiafundTaxRevenue,
foundationPrimary: s.FoundationPrimaryAddress,
foundationFailsafe: s.FoundationFailsafeAddress,
}
Expand Down
12 changes: 6 additions & 6 deletions consensus/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ func (ms *MidState) addSiafundElement(id types.SiafundOutputID, sfo types.Siafun
StateElement: types.StateElement{LeafIndex: types.UnassignedLeafIndex},
ID: id,
SiafundOutput: sfo,
ClaimStart: ms.siafundPool,
ClaimStart: ms.siafundTaxRevenue,
}
ms.sfes = append(ms.sfes, sfe)
ms.created[ms.sfes[len(ms.sfes)-1].ID] = len(ms.sfes) - 1
Expand All @@ -389,7 +389,7 @@ func (ms *MidState) addFileContractElement(id types.FileContractID, fc types.Fil
}
ms.fces = append(ms.fces, fce)
ms.created[ms.fces[len(ms.fces)-1].ID] = len(ms.fces) - 1
ms.siafundPool = ms.siafundPool.Add(ms.base.FileContractTax(fce.FileContract))
ms.siafundTaxRevenue = ms.siafundTaxRevenue.Add(ms.base.FileContractTax(fce.FileContract))
}

func (ms *MidState) reviseFileContractElement(fce types.FileContractElement, rev types.FileContract) {
Expand Down Expand Up @@ -426,7 +426,7 @@ func (ms *MidState) addV2FileContractElement(id types.FileContractID, fc types.V
}
ms.v2fces = append(ms.v2fces, fce)
ms.created[ms.v2fces[len(ms.v2fces)-1].ID] = len(ms.v2fces) - 1
ms.siafundPool = ms.siafundPool.Add(ms.base.V2FileContractTax(fce.V2FileContract))
ms.siafundTaxRevenue = ms.siafundTaxRevenue.Add(ms.base.V2FileContractTax(fce.V2FileContract))
}

func (ms *MidState) reviseV2FileContractElement(fce types.V2FileContractElement, rev types.V2FileContract) {
Expand Down Expand Up @@ -477,7 +477,7 @@ func (ms *MidState) ApplyTransaction(txn types.Transaction, ts V1TransactionSupp
if !ok {
panic("missing SiafundElement")
}
claimPortion := ms.siafundPool.Sub(sfe.ClaimStart).Div64(ms.base.SiafundCount()).Mul64(sfe.SiafundOutput.Value)
claimPortion := ms.siafundTaxRevenue.Sub(sfe.ClaimStart).Div64(ms.base.SiafundCount()).Mul64(sfe.SiafundOutput.Value)
ms.spendSiafundElement(sfe, txid)
ms.addImmatureSiacoinElement(sfi.ParentID.ClaimOutputID(), types.SiacoinOutput{Value: claimPortion, Address: sfi.ClaimAddress})
}
Expand Down Expand Up @@ -528,7 +528,7 @@ func (ms *MidState) ApplyV2Transaction(txn types.V2Transaction) {
}
for _, sfi := range txn.SiafundInputs {
ms.spendSiafundElement(sfi.Parent, txid)
claimPortion := ms.siafundPool.Sub(sfi.Parent.ClaimStart).Div64(ms.base.SiafundCount()).Mul64(sfi.Parent.SiafundOutput.Value)
claimPortion := ms.siafundTaxRevenue.Sub(sfi.Parent.ClaimStart).Div64(ms.base.SiafundCount()).Mul64(sfi.Parent.SiafundOutput.Value)
ms.addImmatureSiacoinElement(sfi.Parent.ID.V2ClaimOutputID(), types.SiacoinOutput{Value: claimPortion, Address: sfi.ClaimAddress})
}
for i, sfo := range txn.SiafundOutputs {
Expand Down Expand Up @@ -736,7 +736,7 @@ func ApplyBlock(s State, b types.Block, bs V1BlockSupplement, targetTimestamp ti

ms := NewMidState(s)
ms.ApplyBlock(b, bs)
s.SiafundPool = ms.siafundPool
s.SiafundTaxRevenue = ms.siafundTaxRevenue
s.Attestations += uint64(len(ms.aes))
s.FoundationPrimaryAddress = ms.foundationPrimary
s.FoundationFailsafeAddress = ms.foundationFailsafe
Expand Down
134 changes: 129 additions & 5 deletions consensus/update_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1089,11 +1089,6 @@ func TestApplyRevertBlockV2(t *testing.T) {
checkUpdateElements(au, addedSCEs, spentSCEs, addedSFEs, spentSFEs)
}

_ = renterPublicKey
_ = hostPublicKey
_ = checkRevertElements
_ = prev

// revert block spending sc and sf
ru := RevertBlock(prev, b2, V1BlockSupplement{})
cs = prev
Expand Down Expand Up @@ -1296,3 +1291,132 @@ func TestApplyRevertBlockV2(t *testing.T) {
checkUpdateElements(au, addedSCEs, spentSCEs, addedSFEs, spentSFEs)
}
}

func TestSiafunds(t *testing.T) {
n, genesisBlock := testnet()
n.HardforkV2.AllowHeight = 1
n.HardforkV2.RequireHeight = 2

key := types.GeneratePrivateKey()

giftAddress := types.StandardAddress(key.PublicKey())
giftAmountSC := types.Siacoins(100e3)
giftAmountSF := uint64(1000)
giftTxn := types.Transaction{
SiacoinOutputs: []types.SiacoinOutput{
{Address: giftAddress, Value: giftAmountSC},
},
SiafundOutputs: []types.SiafundOutput{
{Address: giftAddress, Value: giftAmountSF},
},
}
genesisBlock.Transactions = []types.Transaction{giftTxn}
db, cs := newConsensusDB(n, genesisBlock)

signTxn := func(cs State, txn *types.V2Transaction) {
for i := range txn.SiacoinInputs {
txn.SiacoinInputs[i].SatisfiedPolicy = types.SatisfiedPolicy{
Policy: types.PolicyPublicKey(key.PublicKey()),
Signatures: []types.Signature{key.SignHash(cs.InputSigHash(*txn))},
}
}
for i := range txn.SiafundInputs {
txn.SiafundInputs[i].SatisfiedPolicy = types.SatisfiedPolicy{
Policy: types.PolicyPublicKey(key.PublicKey()),
Signatures: []types.Signature{key.SignHash(cs.InputSigHash(*txn))},
}
}
for i := range txn.FileContracts {
txn.FileContracts[i].RenterSignature = key.SignHash(cs.ContractSigHash(txn.FileContracts[i]))
txn.FileContracts[i].HostSignature = key.SignHash(cs.ContractSigHash(txn.FileContracts[i]))
}
}
mineTxns := func(txns []types.Transaction, v2txns []types.V2Transaction) (au ApplyUpdate, err error) {
b := types.Block{
ParentID: cs.Index.ID,
Timestamp: types.CurrentTimestamp(),
MinerPayouts: []types.SiacoinOutput{{Address: types.VoidAddress, Value: cs.BlockReward()}},
Transactions: txns,
}
if len(v2txns) > 0 {
b.V2 = &types.V2BlockData{
Height: cs.Index.Height + 1,
Commitment: cs.Commitment(cs.TransactionsCommitment(txns, v2txns), b.MinerPayouts[0].Address),
Transactions: v2txns,
}
}
findBlockNonce(cs, &b)
if err = ValidateBlock(cs, b, V1BlockSupplement{}); err != nil {
return
}
cs, au = ApplyBlock(cs, b, V1BlockSupplement{}, db.ancestorTimestamp(b.ParentID))
db.applyBlock(au)
return
}

fc := types.V2FileContract{
ProofHeight: 20,
ExpirationHeight: 30,
RenterOutput: types.SiacoinOutput{Value: types.Siacoins(5000)},
HostOutput: types.SiacoinOutput{Value: types.Siacoins(5000)},
RenterPublicKey: key.PublicKey(),
HostPublicKey: key.PublicKey(),
}
fcValue := fc.RenterOutput.Value.Add(fc.HostOutput.Value).Add(cs.V2FileContractTax(fc))

txn := types.V2Transaction{
SiacoinInputs: []types.V2SiacoinInput{{
Parent: db.sces[giftTxn.SiacoinOutputID(0)],
}},
SiacoinOutputs: []types.SiacoinOutput{{
Address: giftAddress,
Value: giftAmountSC.Sub(fcValue),
}},
FileContracts: []types.V2FileContract{fc},
}
signTxn(cs, &txn)
prev := cs
if _, err := mineTxns(nil, []types.V2Transaction{txn}); err != nil {
t.Fatal(err)
}
// siafund revenue should have increased
if cs.SiafundTaxRevenue != prev.SiafundTaxRevenue.Add(cs.V2FileContractTax(fc)) {
t.Fatalf("expected %v siafund revenue, got %v", prev.SiafundTaxRevenue.Add(cs.V2FileContractTax(fc)), cs.SiafundTaxRevenue)
}

// make a siafund claim
txn = types.V2Transaction{
SiafundInputs: []types.V2SiafundInput{{
Parent: db.sfes[giftTxn.SiafundOutputID(0)],
ClaimAddress: giftAddress,
}},
SiafundOutputs: []types.SiafundOutput{{
Address: giftAddress,
Value: giftAmountSF,
}},
}
signTxn(cs, &txn)
prev = cs
if au, err := mineTxns(nil, []types.V2Transaction{txn}); err != nil {
t.Fatal(err)
} else {
// siafund revenue should be unchanged
if cs.SiafundTaxRevenue != prev.SiafundTaxRevenue {
t.Fatalf("expected %v siafund revenue, got %v", prev.SiafundTaxRevenue, cs.SiafundTaxRevenue)
}
// should have received a timelocked siafund claim output
var claimOutput *types.SiacoinElement
au.ForEachSiacoinElement(func(sce types.SiacoinElement, _, _ bool) {
if sce.ID == txn.SiafundInputs[0].Parent.ID.V2ClaimOutputID() {
claimOutput = &sce
}
})
if claimOutput == nil {
t.Fatal("expected siafund claim output")
} else if claimOutput.MaturityHeight != cs.MaturityHeight()-1 {
t.Fatalf("expected siafund claim output to mature at height %v, got %v", cs.MaturityHeight()-1, claimOutput.MaturityHeight)
} else if exp := cs.V2FileContractTax(fc).Div64(cs.SiafundCount() / giftAmountSF); claimOutput.SiacoinOutput.Value != exp {
t.Fatalf("expected siafund claim output value %v, got %v", exp, claimOutput.SiacoinOutput.Value)
}
}
}
2 changes: 1 addition & 1 deletion types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ type SiafundElement struct {
ID SiafundOutputID `json:"id"`
StateElement StateElement `json:"stateElement"`
SiafundOutput SiafundOutput `json:"siafundOutput"`
ClaimStart Currency `json:"claimStart"` // value of SiafundPool when element was created
ClaimStart Currency `json:"claimStart"` // value of SiafundTaxRevenue when element was created
}

// A FileContractElement is a record of a FileContract within the state
Expand Down
Loading