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

Store v2 transaction miner fee and foundation address #118

Merged
merged 13 commits into from
Oct 22, 2024
3 changes: 3 additions & 0 deletions explorer/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ type V2Transaction struct {
ID types.TransactionID `json:"id"`
ArbitraryData []byte `json:"arbitraryData,omitempty"`

NewFoundationAddress *types.Address `json:"newFoundationAddress,omitempty"`
MinerFee types.Currency `json:"minerFee"`

HostAnnouncements []chain.HostAnnouncement `json:"hostAnnouncements,omitempty"`
}

Expand Down
8 changes: 4 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ toolchain go1.23.2

require (
github.com/mattn/go-sqlite3 v1.14.24
go.sia.tech/core v0.4.7
go.sia.tech/coreutils v0.4.1
go.sia.tech/core v0.4.8-0.20241015191424-3a45c8b415e7
go.sia.tech/coreutils v0.4.2-0.20241007200058-9a2654c61a97
go.sia.tech/jape v0.12.1
go.uber.org/zap v1.27.0
gopkg.in/yaml.v3 v3.0.1
Expand All @@ -23,8 +23,8 @@ require (
go.etcd.io/bbolt v1.3.11 // indirect
go.sia.tech/mux v1.3.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/tools v0.20.0 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
go.sia.tech/core v0.4.7 h1:UAyErZ3nk5/7N0gIG0OEEJJrxh7ru8lgGLlaNtT/Jq0=
go.sia.tech/core v0.4.7/go.mod h1:j2Ke8ihV8or7d2VDrFZWcCkwSVHO0DNMQJAGs9Qop2M=
go.sia.tech/core v0.4.8-0.20241015191424-3a45c8b415e7 h1:xN8iVwYd5/qGYCBHejI5vzxXt8P7kAGelyFK+nKGfSc=
go.sia.tech/core v0.4.8-0.20241015191424-3a45c8b415e7/go.mod h1:CpiFY0jL5OlU6sm/6fwd6/LQe6Ao8G6OtHtq21ggIoA=
go.sia.tech/coreutils v0.4.1 h1:ExQ9g6EtnFe70ptNBG+OtZyFU3aBoEzE/06rtbN6f4c=
go.sia.tech/coreutils v0.4.1/go.mod h1:v60kPqZERsb1ZS0PVe4S8hr2ArNEwTdp7XTzErXnV2U=
go.sia.tech/coreutils v0.4.2-0.20241007200058-9a2654c61a97 h1:DPK4fA7HNdTgb02fsdFHbtaB2+ydy/78M1sHhznQkMw=
go.sia.tech/coreutils v0.4.2-0.20241007200058-9a2654c61a97/go.mod h1:JIaR+zdGZsqPLBM5mVsnwWJ7hBsES+SAEDQg5EFBitM=
go.sia.tech/jape v0.12.1 h1:xr+o9V8FO8ScRqbSaqYf9bjj1UJ2eipZuNcI1nYousU=
go.sia.tech/jape v0.12.1/go.mod h1:wU+h6Wh5olDjkPXjF0tbZ1GDgoZ6VTi4naFw91yyWC4=
go.sia.tech/mux v1.3.0 h1:hgR34IEkqvfBKUJkAzGi31OADeW2y7D6Bmy/Jcbop9c=
Expand All @@ -35,12 +39,16 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
53 changes: 51 additions & 2 deletions internal/testutil/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,18 @@ func CreateAnnouncement(priv types.PrivateKey, netAddress string) []byte {
// MineBlock mines sets the metadata fields of the block along with the
// transactions and then generates a valid nonce for the block.
func MineBlock(state consensus.State, txns []types.Transaction, minerAddr types.Address) types.Block {
reward := state.BlockReward()
for _, txn := range txns {
for _, fee := range txn.MinerFees {
reward = reward.Add(fee)
}
}

b := types.Block{
ParentID: state.Index.ID,
Timestamp: types.CurrentTimestamp(),
Transactions: txns,
MinerPayouts: []types.SiacoinOutput{{Address: minerAddr, Value: state.BlockReward()}},
MinerPayouts: []types.SiacoinOutput{{Address: minerAddr, Value: reward}},
}
if !coreutils.FindBlockNonce(state, &b, time.Minute) {
panic("failed to mine test block quickly enough")
Expand All @@ -93,10 +100,15 @@ func MineBlock(state consensus.State, txns []types.Transaction, minerAddr types.
// MineV2Block mines sets the metadata fields of the block along with the
// transactions and then generates a valid nonce for the block.
func MineV2Block(state consensus.State, txns []types.V2Transaction, minerAddr types.Address) types.Block {
reward := state.BlockReward()
for _, txn := range txns {
reward = reward.Add(txn.MinerFee)
}

b := types.Block{
ParentID: state.Index.ID,
Timestamp: types.CurrentTimestamp(),
MinerPayouts: []types.SiacoinOutput{{Address: minerAddr, Value: state.BlockReward()}},
MinerPayouts: []types.SiacoinOutput{{Address: minerAddr, Value: reward}},

V2: &types.V2BlockData{
Transactions: txns,
Expand Down Expand Up @@ -142,3 +154,40 @@ func SignTransaction(cs consensus.State, pk types.PrivateKey, txn *types.Transac
}
SignTransactionWithContracts(cs, pk, types.PrivateKey{}, types.PrivateKey{}, txn)
}

// SignV2TransactionWithContracts signs a transaction using the specified
// private keys, including contracts and revisions.
func SignV2TransactionWithContracts(cs consensus.State, pk, renterPK, hostPK types.PrivateKey, txn *types.V2Transaction) {
for i := range txn.SiacoinInputs {
txn.SiacoinInputs[i].SatisfiedPolicy.Signatures = []types.Signature{pk.SignHash(cs.InputSigHash(*txn))}
}
for i := range txn.SiafundInputs {
txn.SiafundInputs[i].SatisfiedPolicy.Signatures = []types.Signature{pk.SignHash(cs.InputSigHash(*txn))}
}
for i := range txn.FileContracts {
txn.FileContracts[i].RenterSignature = renterPK.SignHash(cs.ContractSigHash(txn.FileContracts[i]))
txn.FileContracts[i].HostSignature = hostPK.SignHash(cs.ContractSigHash(txn.FileContracts[i]))
}
for i := range txn.FileContractRevisions {
txn.FileContractRevisions[i].Revision.RenterSignature = renterPK.SignHash(cs.ContractSigHash(txn.FileContractRevisions[i].Revision))
txn.FileContractRevisions[i].Revision.HostSignature = hostPK.SignHash(cs.ContractSigHash(txn.FileContractRevisions[i].Revision))
}
for i := range txn.FileContractResolutions {
switch r := txn.FileContractResolutions[i].Resolution.(type) {
case *types.V2FileContractRenewal:
r.RenterSignature = renterPK.SignHash(cs.RenewalSigHash(*r))
r.HostSignature = hostPK.SignHash(cs.RenewalSigHash(*r))
case *types.V2FileContractFinalization:
*r = types.V2FileContractFinalization(renterPK.SignHash(cs.ContractSigHash(txn.FileContractResolutions[i].Parent.V2FileContract)))
}
}
}

// SignV2Transaction signs a transaction that does not have any contracts with
// the specified private key.
func SignV2Transaction(cs consensus.State, pk types.PrivateKey, txn *types.V2Transaction) {
if len(txn.FileContracts) > 0 || len(txn.FileContractRevisions) > 0 {
panic("use SignV2TransactionWithContracts instead")
}
SignV2TransactionWithContracts(cs, pk, types.PrivateKey{}, types.PrivateKey{}, txn)
}
3 changes: 3 additions & 0 deletions internal/testutil/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ func CheckTransaction(t *testing.T, expectTxn types.Transaction, gotTxn explorer
func CheckV2Transaction(t *testing.T, expectTxn types.V2Transaction, gotTxn explorer.V2Transaction) {
t.Helper()

Equal(t, "new foundation address", expectTxn.NewFoundationAddress, gotTxn.NewFoundationAddress)
Equal(t, "miner fee", expectTxn.MinerFee, gotTxn.MinerFee)

Equal(t, "arbitrary data", len(expectTxn.ArbitraryData), len(gotTxn.ArbitraryData))

for i := range expectTxn.ArbitraryData {
Expand Down
2 changes: 1 addition & 1 deletion persist/sqlite/addresses.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func scanEvent(tx *txn, s scanner) (ev explorer.Event, eventID int64, err error)
if err != nil {
return explorer.Event{}, 0, fmt.Errorf("failed to fetch v2 transaction ID: %w", err)
}
txns, err := getV2Transactions(tx, map[int64]transactionID{txnID: {id: types.TransactionID(ev.ID)}})
txns, err := getV2Transactions(tx, []types.TransactionID{types.TransactionID(ev.ID)})
if err != nil || len(txns) == 0 {
return explorer.Event{}, 0, fmt.Errorf("failed to fetch v2 transaction: %w", err)
}
Expand Down
6 changes: 5 additions & 1 deletion persist/sqlite/consensus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2533,7 +2533,11 @@ func TestMetricCirculatingSupply(t *testing.T) {

cm := chain.NewManager(store, genesisState)

circulatingSupply := genesisState.FoundationSubsidy().Value
var circulatingSupply types.Currency
if subsidy, ok := genesisState.FoundationSubsidy(); ok {
circulatingSupply = circulatingSupply.Add(subsidy.Value)
}

for _, txn := range genesisBlock.Transactions {
for _, sco := range txn.SiacoinOutputs {
circulatingSupply = circulatingSupply.Add(sco.Value)
Expand Down
17 changes: 6 additions & 11 deletions persist/sqlite/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -255,28 +255,23 @@ CREATE INDEX transaction_file_contract_revisions_transaction_id_index ON transac

CREATE TABLE v2_transactions (
id INTEGER PRIMARY KEY,
transaction_id BLOB UNIQUE NOT NULL
transaction_id BLOB UNIQUE NOT NULL,

new_foundation_address BLOB,
chris124567 marked this conversation as resolved.
Show resolved Hide resolved
miner_fee BLOB NOT NULL,
arbitrary_data BLOB
);
CREATE INDEX v2_transactions_transaction_id_index ON v2_transactions(transaction_id);

CREATE TABLE v2_block_transactions (
block_id BLOB REFERENCES blocks(id) ON DELETE CASCADE NOT NULL,
transaction_id INTEGER REFERENCES v2_transactions(id) ON DELETE CASCADE NOT NULL,
block_order INTEGER NOT NULL,
transaction_id INTEGER REFERENCES v2_transactions(id) ON DELETE CASCADE NOT NULL,
UNIQUE(block_id, block_order)
);
CREATE INDEX v2_block_transactions_block_id_index ON v2_block_transactions(block_id);
CREATE INDEX v2_block_transactions_transaction_id_index ON v2_block_transactions(transaction_id);
CREATE INDEX v2_block_transactions_transaction_id_block_id ON v2_block_transactions(transaction_id, block_id);

CREATE TABLE v2_transaction_arbitrary_data (
transaction_id INTEGER REFERENCES v2_transactions(id) ON DELETE CASCADE NOT NULL,
data BLOB NOT NULL,
UNIQUE(transaction_id)
);

CREATE INDEX v2_transaction_arbitrary_data_transaction_id_index ON v2_transaction_arbitrary_data(transaction_id);

CREATE TABLE state_tree (
row INTEGER NOT NULL,
column INTEGER NOT NULL,
Expand Down
4 changes: 2 additions & 2 deletions persist/sqlite/merkle.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import (
func (s *Store) MerkleProof(leafIndex uint64) (proof []types.Hash256, err error) {
err = s.transaction(func(tx *txn) error {
var numLeaves uint64
if err := tx.QueryRow("SELECT COUNT(*) FROM state_tree WHERE i = 0").Scan(&numLeaves); err != nil {
if err := tx.QueryRow("SELECT COUNT(*) FROM state_tree WHERE row = 0").Scan(&numLeaves); err != nil {
chris124567 marked this conversation as resolved.
Show resolved Hide resolved
return err
}

pos := leafIndex
stmt, err := tx.Prepare("SELECT hash FROM state_tree WHERE row = ? AND column = ?")
stmt, err := tx.Prepare("SELECT value FROM state_tree WHERE row = ? AND column = ?")
if err != nil {
return err
}
Expand Down
27 changes: 7 additions & 20 deletions persist/sqlite/v2consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,14 @@ import (
"go.sia.tech/explored/explorer"
)

func addV2ArbitraryData(tx *txn, id int64, txn types.V2Transaction) error {
stmt, err := tx.Prepare(`INSERT INTO v2_transaction_arbitrary_data(transaction_id, data) VALUES (?, ?)`)

if err != nil {
return fmt.Errorf("addV2ArbitraryData: failed to prepare statement: %w", err)
}
defer stmt.Close()

if _, err := stmt.Exec(id, txn.ArbitraryData); err != nil {
return fmt.Errorf("addV2ArbitraryData: failed to execute statement: %w", err)
}
return nil
}

func addV2Transactions(tx *txn, bid types.BlockID, txns []types.V2Transaction) (map[types.TransactionID]txnDBId, error) {
checkTransactionStmt, err := tx.Prepare(`SELECT id FROM v2_transactions WHERE transaction_id = ?`)
if err != nil {
return nil, fmt.Errorf("failed to prepare check v2_transaction statement: %v", err)
}
defer checkTransactionStmt.Close()

insertTransactionStmt, err := tx.Prepare(`INSERT INTO v2_transactions (transaction_id) VALUES (?)`)
insertTransactionStmt, err := tx.Prepare(`INSERT INTO v2_transactions (transaction_id, new_foundation_address, miner_fee, arbitrary_data) VALUES (?, ?, ?, ?)`)
if err != nil {
return nil, fmt.Errorf("failed to prepare insert v2_transaction statement: %v", err)
}
Expand All @@ -52,7 +38,12 @@ func addV2Transactions(tx *txn, bid types.BlockID, txns []types.V2Transaction) (
}

if !exist {
result, err := insertTransactionStmt.Exec(encode(txn.ID()))
var newFoundationAddress any
if txn.NewFoundationAddress != nil {
newFoundationAddress = encode(txn.NewFoundationAddress)
}

result, err := insertTransactionStmt.Exec(encode(txn.ID()), newFoundationAddress, encode(txn.MinerFee), txn.ArbitraryData)
if err != nil {
return nil, fmt.Errorf("failed to insert into v2_transactions: %w", err)
}
Expand Down Expand Up @@ -81,10 +72,6 @@ func addV2TransactionFields(tx *txn, txns []types.V2Transaction, scDBIds map[typ
if dbID.exist {
continue
}

if err := addV2ArbitraryData(tx, dbID.id, txn); err != nil {
return fmt.Errorf("failed to add arbitrary data: %w", err)
}
}

return nil
Expand Down
Loading
Loading