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

Add server-side check for insanely high fees. #63

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
43 changes: 34 additions & 9 deletions coinjoin/coinjoin.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,23 @@ func (t *Tx) UnmarshalBinary(b []byte) error {
return t.Tx.Deserialize(bytes.NewReader(b))
}

var bogusSigScript = make([]byte, 108) // worst case size to redeem a P2PKH

func (t *Tx) estimateSerializeSize(tx *wire.MsgTx, mcount int) int {
bogusMixedOut := &wire.TxOut{
Value: t.mixValue,
Version: t.sc.version(),
PkScript: make([]byte, t.sc.scriptSize()),
}
for _, in := range tx.TxIn {
in.SignatureScript = bogusSigScript
}
for i := 0; i < mcount; i++ {
tx.AddTxOut(bogusMixedOut)
}
return tx.SerializeSize()
}

func feeForSerializeSize(relayFeePerKb int64, txSerializeSize int) int64 {
fee := relayFeePerKb * int64(txSerializeSize) / 1000

Expand All @@ -220,6 +237,17 @@ func feeForSerializeSize(relayFeePerKb int64, txSerializeSize int) int64 {
return fee
}

// highFeeRate is the maximum multiplier of the standard fee rate before unmixed
// data is refused for paying too high of a fee. It should not be too low such
// that mixing outputs at the smallest common mixed value errors for too high
// fees, as these mixes are performed without any change outputs.
const highFeeRate = 150

func paysHighFees(fee, relayFeePerKb int64, txSerializeSize int) bool {
maxFee := feeForSerializeSize(highFeeRate*relayFeePerKb, txSerializeSize)
return fee > maxFee
}

func (t *Tx) ValidateUnmixed(unmixed []byte, mcount int) error {
var fee int64
other := new(wire.MsgTx)
Expand Down Expand Up @@ -253,18 +281,15 @@ func (t *Tx) ValidateUnmixed(unmixed []byte, mcount int) error {
return err
}
fee -= int64(mcount) * t.mixValue
bogusMixedOut := &wire.TxOut{
Value: t.mixValue,
Version: t.sc.version(),
PkScript: make([]byte, t.sc.scriptSize()),
}
for i := 0; i < mcount; i++ {
other.AddTxOut(bogusMixedOut)
}
requiredFee := feeForSerializeSize(t.feeRate, other.SerializeSize())
size := t.estimateSerializeSize(other, mcount)
requiredFee := feeForSerializeSize(t.feeRate, size)
if fee < requiredFee {
return errors.New("coinjoin: unmixed transaction does not pay enough network fees")
}
if paysHighFees(fee, t.feeRate, size) {
return errors.New("coinjoin: unmixed transaction pays insanely high fees")
}

return nil
}

Expand Down
2 changes: 1 addition & 1 deletion integration/honest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func TestHonest(t *testing.T) {
i := i
go func() {
input := &wire.TxIn{ValueIn: inputValue * 1e8}
change := &wire.TxOut{Value: 1e8 - int64(1+i)*0.001e8, PkScript: change}
change := &wire.TxOut{Value: 1e8 - 0.0001e8 + int64(i+1), PkScript: change}
con := newConfirmer(input, change)
conn, err := tls.Dial("tcp", s.Addr, nettest.ClientTLS)
if err != nil {
Expand Down