Skip to content

Commit

Permalink
change PaysToPubkey to Auth, returning only error on failure
Browse files Browse the repository at this point in the history
Also changes UTXO.ScriptSize to UTXO.SpendSize, since the size is
more than just the sig script.
  • Loading branch information
buck54321 committed Sep 6, 2019
1 parent f7a665e commit 7952a78
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 61 deletions.
11 changes: 6 additions & 5 deletions server/asset/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,12 @@ type UTXO interface {
// should have one confirmation. A negative number can be returned if error
// is not nil.
Confirmations() (int64, error)
// PaysToPubkeys checks that the provided pubkeys can spend the UTXO and
// the signatures are valid.
PaysToPubkeys(pubkeys, sigs [][]byte, msg []byte) (bool, error)
// ScriptSize returns the UTXO's maximum sigScript byte count.
ScriptSize() uint32
// Auth checks that the owner of the provided pubkeys can spend the UTXO.
// The signatures (sigs) generated with the private keys corresponding
// to pubkeys must validate against the pubkeys and signing message (msg).
Auth(pubkeys, sigs [][]byte, msg []byte) error
// SpendSize returns the size of the serialized input that spends this UTXO.
SpendSize() uint32
// TxHash is a byte-slice of the UTXO's transaction hash.
TxHash() []byte
// TxID is a string identifier for the transaction, typically a hexadecimal
Expand Down
70 changes: 28 additions & 42 deletions server/asset/dcr/dcr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -697,10 +697,10 @@ func TestUTXOs(t *testing.T) {
if err != nil {
t.Fatalf("case 1 - unexpected error: %v", err)
}
// While we're here, check the spend script size is correct.
scriptSize := utxo.ScriptSize()
if scriptSize != P2PKHSigScriptSize+txInOverhead {
t.Fatalf("case 1 - unexpected spend script size reported. expected %d, got %d", P2PKHSigScriptSize, scriptSize)
// While we're here, check the spend size is correct.
spendSize := utxo.SpendSize()
if spendSize != P2PKHSigScriptSize+txInOverhead {
t.Fatalf("case 1 - unexpected spend script size reported. expected %d, got %d", P2PKHSigScriptSize, spendSize)
}
// Now "mine" the transaction.
testAddBlockVerbose(blockHash, 1, txHeight, 1)
Expand All @@ -716,12 +716,9 @@ func TestUTXOs(t *testing.T) {
t.Fatalf("case 1 - expected 1 confirmation after mining transaction, found %d", confs)
}
// Make sure the pubkey spends the output.
spends, err := utxo.PaysToPubkeys([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg)
err = utxo.Auth([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg)
if err != nil {
t.Fatalf("case 1 - PaysToPubkeys error: %v", err)
}
if !spends {
t.Fatalf("case 1 - false returned from PaysToPubkeys")
t.Fatalf("case 1 - Auth error: %v", err)
}

// CASE 2: A valid UTXO in a mined block. This UTXO will have non-zero
Expand All @@ -736,12 +733,9 @@ func TestUTXOs(t *testing.T) {
if err != nil {
t.Fatalf("case 2 - unexpected error for sig type %d: %v", int(sigType), err)
}
spends, err = utxo.PaysToPubkeys([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg)
err = utxo.Auth([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg)
if err != nil {
t.Fatalf("case 2 - PaysToPubkeys error with sig type %d: %v", int(sigType), err)
}
if !spends {
t.Fatalf("case 2 - false returned from PaysToPubkeys for sig type %d", int(sigType))
t.Fatalf("case 2 - Auth error with sig type %d: %v", int(sigType), err)
}
}

Expand Down Expand Up @@ -825,12 +819,9 @@ func TestUTXOs(t *testing.T) {
t.Fatalf("case 6 - unexpected error after maturing block: %v", err)
}
// Since this is our first stake transaction, let's check the pubkey
spends, err = utxo.PaysToPubkeys([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg)
err = utxo.Auth([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg)
if err != nil {
t.Fatalf("case 6 - PaysToPubkeys error: %v", err)
}
if !spends {
t.Fatalf("case 6 - false returned from PaysToPubkeys")
t.Fatalf("case 6 - Auth error: %v", err)
}

// CASE 7: A UTXO that becomes invalid in a reorg
Expand Down Expand Up @@ -917,12 +908,9 @@ func TestUTXOs(t *testing.T) {
if confs != 1 {
t.Fatalf("case 9 - expected 1 confirmation, got %d", confs)
}
spends, err = utxo.PaysToPubkeys(msgMultiSig.auth.pubkeys[:1], msgMultiSig.auth.sigs[:1], msgMultiSig.auth.msg)
err = utxo.Auth(msgMultiSig.auth.pubkeys[:1], msgMultiSig.auth.sigs[:1], msgMultiSig.auth.msg)
if err != nil {
t.Fatalf("case 9 - PaysToPubkeys error: %v", err)
}
if !spends {
t.Fatalf("case 9 - false returned from PaysToPubkeys")
t.Fatalf("case 9 - Auth error: %v", err)
}

// CASE 10: A UTXO with a pay-to-script-hash for a 2-of-2 multisig redeem
Expand All @@ -937,17 +925,21 @@ func TestUTXOs(t *testing.T) {
t.Fatalf("case 10 - received error for utxo: %v", err)
}
// Try to get by with just one of the pubkeys.
_, err = utxo.PaysToPubkeys(msgMultiSig.auth.pubkeys[:1], msgMultiSig.auth.sigs[:1], msgMultiSig.auth.msg)
err = utxo.Auth(msgMultiSig.auth.pubkeys[:1], msgMultiSig.auth.sigs[:1], msgMultiSig.auth.msg)
if err == nil {
t.Fatalf("case 10 - no error when only provided one of two required pubkeys")
t.Fatalf("case 10 - no Auth error when only provided one of two required pubkeys")
}
// Now do both.
spends, err = utxo.PaysToPubkeys(msgMultiSig.auth.pubkeys, msgMultiSig.auth.sigs, msgMultiSig.auth.msg)
err = utxo.Auth(msgMultiSig.auth.pubkeys, msgMultiSig.auth.sigs, msgMultiSig.auth.msg)
if err != nil {
t.Fatalf("case 10 - PaysToPubkeys error: %v", err)
t.Fatalf("case 10 - Auth error: %v", err)
}
if !spends {
t.Fatalf("case 10 - false returned from PaysToPubkeys")
// Try with a duplicate pubkey and signature.
dupeKeys := [][]byte{msgMultiSig.auth.pubkeys[0], msgMultiSig.auth.pubkeys[0]}
dupeSigs := [][]byte{msgMultiSig.auth.sigs[0], msgMultiSig.auth.sigs[0]}
err = utxo.Auth(dupeKeys, dupeSigs, msgMultiSig.auth.msg)
if err == nil {
t.Fatalf("case 10 - no Auth error with duplicate keys/sigs")
}

// CASE 11: A UTXO with a pay-to-script-hash for a P2PKH redeem script.
Expand All @@ -967,17 +959,14 @@ func TestUTXOs(t *testing.T) {
t.Fatalf("case 11 - stake p2sh not marked as stake")
}
// Give it nonsense.
_, err = utxo.PaysToPubkeys([][]byte{randomBytes(33)}, [][]byte{randomBytes(33)}, randomBytes(32))
err = utxo.Auth([][]byte{randomBytes(33)}, [][]byte{randomBytes(33)}, randomBytes(32))
if err == nil {
t.Fatalf("case 11 - no error when providing nonsense pubkey")
t.Fatalf("case 11 - no Auth error when providing nonsense pubkey")
}
// Now give it the right one.
spends, err = utxo.PaysToPubkeys([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg)
err = utxo.Auth([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg)
if err != nil {
t.Fatalf("case 11 - PaysToPubkeys error: %v", err)
}
if !spends {
t.Fatalf("case 11 - false returned from PaysToPubkeys")
t.Fatalf("case 11 - Auth error: %v", err)
}

// CASE 12: A revocation.
Expand All @@ -997,12 +986,9 @@ func TestUTXOs(t *testing.T) {
t.Fatalf("case 12 - stake p2sh not marked as stake")
}
// Check the pubkey.
spends, err = utxo.PaysToPubkeys([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg)
err = utxo.Auth([][]byte{msg.auth.pubkey}, [][]byte{msg.auth.sig}, msg.auth.msg)
if err != nil {
t.Fatalf("case 12 - PaysToPubkeys error: %v", err)
}
if !spends {
t.Fatalf("case 12 - false returned from PaysToPubkeys")
t.Fatalf("case 12 - Auth error: %v", err)
}
}

Expand Down
28 changes: 14 additions & 14 deletions server/asset/dcr/utxo.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,43 +129,43 @@ func (utxo *UTXO) Confirmations() (int64, error) {
return int64(confs), nil
}

// PaysToPubkeys verifies that the utxo pays to the supplied public key(s). This
// is an asset.DEXAsset method.
func (utxo *UTXO) PaysToPubkeys(pubkeys, sigs [][]byte, msg []byte) (bool, error) {
// Auth verifies that the utxo pays to the supplied public key(s). This is an
// asset.DEXAsset method.
func (utxo *UTXO) Auth(pubkeys, sigs [][]byte, msg []byte) error {
if len(pubkeys) < utxo.numSigs {
return false, fmt.Errorf("not enough signatures for utxo %s:%d. expected %d, got %d", utxo.txHash, utxo.vout, utxo.numSigs, len(pubkeys))
return fmt.Errorf("not enough signatures for utxo %s:%d. expected %d, got %d", utxo.txHash, utxo.vout, utxo.numSigs, len(pubkeys))
}
evalScript := utxo.pkScript
if utxo.scriptType.isP2SH() {
evalScript = utxo.redeemScript
}
scriptAddrs, err := extractScriptAddrs(evalScript)
if err != nil {
return false, err
return err
}
if scriptAddrs.nRequired != utxo.numSigs {
return false, fmt.Errorf("signature requirement mismatch for utxo %s:%d. %d != %d", utxo.txHash, utxo.vout, scriptAddrs.nRequired, utxo.numSigs)
return fmt.Errorf("signature requirement mismatch for utxo %s:%d. %d != %d", utxo.txHash, utxo.vout, scriptAddrs.nRequired, utxo.numSigs)
}
matches, err := pkMatches(pubkeys, scriptAddrs.pubkeys, nil)
if err != nil {
return false, fmt.Errorf("error during pubkey matching: %v", err)
return fmt.Errorf("error during pubkey matching: %v", err)
}
m, err := pkMatches(pubkeys, scriptAddrs.pkHashes, dcrutil.Hash160)
if err != nil {
return false, fmt.Errorf("error during pubkey hash matching: %v", err)
return fmt.Errorf("error during pubkey hash matching: %v", err)
}
matches = append(matches, m...)
if len(matches) < utxo.numSigs {
return false, fmt.Errorf("not enough pubkey matches to satisfy the script for utxo %s:%d. expected %d, got %d", utxo.txHash, utxo.vout, utxo.numSigs, len(matches))
return fmt.Errorf("not enough pubkey matches to satisfy the script for utxo %s:%d. expected %d, got %d", utxo.txHash, utxo.vout, utxo.numSigs, len(matches))
}
for _, match := range matches {
err := checkSig(msg, match.pubkey, sigs[match.idx], match.sigType)
if err != nil {
return false, err
return err
}

}
return true, nil
return nil
}

type pkMatch struct {
Expand Down Expand Up @@ -216,9 +216,9 @@ func pkMatches(pubkeys [][]byte, addrs []dcrutil.Address, hasher func([]byte) []
return matches, nil
}

// ScriptSize returns the maximum spend script size of the UTXO, in bytes.
// This is a method of the asset.UTXO interface.
func (utxo *UTXO) ScriptSize() uint32 {
// SpendSize returns the maximum size of the serialized TxIn that spends this
// UTXO, in bytes. This is a method of the asset.UTXO interface.
func (utxo *UTXO) SpendSize() uint32 {
return utxo.spendSize
}

Expand Down

0 comments on commit 7952a78

Please sign in to comment.