-
Notifications
You must be signed in to change notification settings - Fork 2.5k
multi: recognize new standard P2A output type #2433
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
base: master
Are you sure you want to change the base?
Conversation
Pull Request Test Coverage Report for Build 20117557279Details
💛 - Coveralls |
sputn1ck
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
| return nil, fmt.Errorf("unable to parse address: %v", err) | ||
| } | ||
|
|
||
| // P2A scripts don't have addresses. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is an address for p2a outputs bc1pfeessrawgf
https://mempool.space/address/bc1pfeessrawgf
https://github.com/bitcoin/bitcoin/blob/25212dfdb4cd7291392b6a94130f658c5bfa0a48/test/functional/rpc_validateaddress.py#L171
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like a stray line, but ExtractPkScriptAddrs should handle that. Will add a test for this.
| nilAddrErrStr) | ||
| } | ||
| return payToWitnessTaprootScript(addr.ScriptAddress()) | ||
| case *btcutil.AddressPayToAnchor: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit
btcutil.AddressPayToAnchor isn't introduced until commit 3f99cfe0a9b0ed9a33e0c11136127e8b6e2906ea.
So this commit doesn't build.
| }, | ||
| { | ||
| name: "invalid - wrong version", | ||
| script: []byte{OP_0, OP_DATA_2, 0x4e, 0x73}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a question: is OP_1 a version? Curious as to the test's name
nvm figured it out
|
|
||
| go 1.23.2 | ||
|
|
||
| replace github.com/btcsuite/btcd/btcutil => ./btcutil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we ok with this or is this temporary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is temp. It's needed as we ref the newly added addr type in txscript/standard.go.
#1825 will resolve this circualr dep module issue. After this is merged, I'll be ale to update the module dep, then remove this.
btcutil/p2a_address_test.go
Outdated
| otherNet = &chaincfg.TestNet3Params | ||
| } | ||
| if addr.IsForNet(otherNet) { | ||
| t.Errorf("IsForNet(other) = true, want false") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's this testing for? Why do we want to test "othernet-ness"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just that IsForNet properly detects if an address isn't for the target network.
I agree it's a trivial test, tbh I did it just so the test coverage reported is higher.
btcutil/p2a_address_test.go
Outdated
| t.Errorf("IsForNet(other) = true, want false") | ||
| } | ||
|
|
||
| expectedScript := []byte{0x51, 0x02, 0x4e, 0x73} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better if I we declare something like the declaration in txscript/standard.go
var PayToAnchorScript = []byte{OP_1, OP_DATA_2, 0x4e, 0x73}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah the issue with that again is the module issue I brought up which will be addressed by #1825. This is defined, but the btcutil module wasn't updated (as this was only added in this PR). So I had to add yet another replace to amek things work.
btcutil/address.go
Outdated
| // script bytes: OP_1 <0x4e73>. | ||
| func (a *AddressPayToAnchor) ScriptAddress() []byte { | ||
| // Return the fixed P2A script bytes. | ||
| return []byte{0x51, 0x02, 0x4e, 0x73} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah here too. Would be better if we declared some variable and referred to it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually this won't work, as it causes an import cycle. We can't import txscript from btcutil. The other changed worked at it was in btcutil_test.
In this commit, we modify the execution logic, such that the P2A witness program doesn't appear as a unknown witness program version. For execution, we just make sure the script passes.
P2A is standard if the witness and the sigscript is empty. We make a smol refactor to be able to write a unit test for the input standardness.
We make sure it'll be accepted into the mempool, and can be spent without a witness.
|
@kcalvinalvin PTAL |
| // - Regtest: bcrt1pfeesnyr2tx | ||
| // - Simnet: sb1pfeesxv0pfa | ||
| func (a *AddressPayToAnchor) EncodeAddress() string { | ||
| // For unknown networks, generate the address from the anchor data. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird comment. Should remove this.
| require.Equal(t, tt.wantAddress, addr.String()) | ||
| require.True(t, addr.IsForNet(tt.net)) | ||
|
|
||
| // Verify it's not for a different network. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The require.True() does cover this and we don't need this extra check but it's ok.
Claude could be better but heh.
| if IsPayToAnchorScript(pkScript) { | ||
| // P2A scripts don't have traditional addresses and require no | ||
| // signatures (anyone can spend them). | ||
| return PayToAnchorTy, nil, 0, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should be returning the address here because of this. 2eadd59#r2390989822
| class, PayToAnchorTy) | ||
| } | ||
|
|
||
| if len(addrs) != 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'd want 1 address since they P2A has an address
| class.String()) | ||
| } | ||
|
|
||
| // Test that regular taproot scripts are still recognized as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird thing to test for here but ok
| } | ||
|
|
||
| // LookupEntry returns the entry for a given outpoint. | ||
| func (m *mockUtxoView) LookupEntry(op wire.OutPoint) utxoEntry { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like these mock utxo entry and mock utxoview is only needed for the tests but it really doesn't need this abstraction. Not a blocker but yeah I think simpler is better.
diff --git a/mempool/policy.go b/mempool/policy.go
index 798d6686..46339d33 100644
--- a/mempool/policy.go
+++ b/mempool/policy.go
@@ -79,29 +79,6 @@ func calcMinRequiredTxRelayFee(serializedSize int64, minRelayTxFee btcutil.Amoun
return minFee
}
-// utxoEntry is an interface that provides access to UTXO data needed for
-// input standardness checks.
-type utxoEntry interface {
- PkScript() []byte
-}
-
-// utxoView is an interface that provides access to UTXOs needed for
-// input standardness checks.
-type utxoView interface {
- LookupEntry(wire.OutPoint) utxoEntry
-}
-
-// utxoViewpointAdapter wraps a blockchain.UtxoViewpoint to implement the
-// utxoView interface.
-type utxoViewpointAdapter struct {
- view *blockchain.UtxoViewpoint
-}
-
-// LookupEntry returns the entry for a given outpoint.
-func (u *utxoViewpointAdapter) LookupEntry(op wire.OutPoint) utxoEntry {
- return u.view.LookupEntry(op)
-}
-
// checkInputsStandard performs a series of checks on a transaction's inputs
// to ensure they are "standard". A standard transaction input within the
// context of this function is one whose referenced public key script is of a
@@ -113,12 +90,6 @@ func (u *utxoViewpointAdapter) LookupEntry(op wire.OutPoint) utxoEntry {
// accurately and concisely via the txscript.ScriptVerifyCleanStack and
// txscript.ScriptVerifySigPushOnly flags.
func checkInputsStandard(tx *btcutil.Tx, utxoView *blockchain.UtxoViewpoint) error {
- return checkInputsStandardWithView(tx, &utxoViewpointAdapter{view: utxoView})
-}
-
-// checkInputsStandardWithView performs input standardness checks using the
-// utxoView interface.
-func checkInputsStandardWithView(tx *btcutil.Tx, utxoView utxoView) error {
// NOTE: The reference implementation also does a coinbase check here,
// but coinbases have already been rejected prior to calling this
// function so no need to recheck.
diff --git a/mempool/policy_test.go b/mempool/policy_test.go
index c3259fd0..3411fa08 100644
--- a/mempool/policy_test.go
+++ b/mempool/policy_test.go
@@ -9,13 +9,13 @@ import (
"testing"
"time"
+ "github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire"
- "github.com/stretchr/testify/mock"
)
// TestCalcMinRequiredTxRelayFee tests the calcMinRequiredTxRelayFee API.
@@ -660,44 +660,28 @@ func TestCheckTransactionStandard(t *testing.T) {
}
}
-// mockUtxoEntry mocks the utxoEntry interface using testify/mock.
-type mockUtxoEntry struct {
- mock.Mock
-}
-
-// PkScript returns the public key script.
-func (m *mockUtxoEntry) PkScript() []byte {
- args := m.Called()
- return args.Get(0).([]byte)
-}
-
-// mockUtxoView mocks the utxoView interface using testify/mock.
-type mockUtxoView struct {
- mock.Mock
-}
-
-// LookupEntry returns the entry for a given outpoint.
-func (m *mockUtxoView) LookupEntry(op wire.OutPoint) utxoEntry {
- args := m.Called(op)
- if args.Get(0) == nil {
- return nil
- }
- return args.Get(0).(utxoEntry)
-}
-
// TestP2ASpendingStandardness tests that P2A outputs require empty witness
// and empty signature script to be considered standard.
func TestP2ASpendingStandardness(t *testing.T) {
// Create a previous transaction with a P2A output.
- prevTxHash, _ := chainhash.NewHashFromStr("0101010101010101010101010101010101010101010101010101010101010101")
- prevOut := wire.OutPoint{Hash: *prevTxHash, Index: 0}
+ prevTx := wire.NewMsgTx(2)
+ prevTx.AddTxIn(&wire.TxIn{
+ PreviousOutPoint: wire.OutPoint{
+ Hash: chainhash.Hash{},
+ Index: 0,
+ },
+ })
+ prevTx.AddTxOut(&wire.TxOut{
+ Value: 1000,
+ PkScript: txscript.PayToAnchorScript,
+ })
+ prevBtcTx := btcutil.NewTx(prevTx)
- // Create mocked UTXO entry and view.
- mockEntry := new(mockUtxoEntry)
- mockEntry.On("PkScript").Return(txscript.PayToAnchorScript)
+ // Create a UTXO view with the P2A output.
+ utxoView := blockchain.NewUtxoViewpoint()
+ utxoView.AddTxOuts(prevBtcTx, 100)
- mockView := new(mockUtxoView)
- mockView.On("LookupEntry", prevOut).Return(mockEntry)
+ prevOut := wire.OutPoint{Hash: *prevBtcTx.Hash(), Index: 0}
tests := []struct {
name string
@@ -756,7 +740,7 @@ func TestP2ASpendingStandardness(t *testing.T) {
})
btcTx := btcutil.NewTx(tx)
- err := checkInputsStandardWithView(btcTx, mockView)
+ err := checkInputsStandard(btcTx, utxoView)
if test.shouldFail {
if err == nil {
@@ -767,10 +751,6 @@ func TestP2ASpendingStandardness(t *testing.T) {
t.Errorf("Unexpected error for valid P2A spend: %v", err)
}
}
-
- // Verify that the mock was called.
- mockView.AssertExpectations(t)
- mockEntry.AssertExpectations(t)
})
}
}|
Most things look good. But Things done:
|
In this PR, we add initial recognition of the new P2A output type. It actually overloads the existing Taproot witness version, but with a distinct witness program. We adopt the proposed standardness rules to make only spends without any witness data standard. Utilities to parse the script, make addresses, etc - have also been added.
Note that this doesn't include the logic to support "ephemeral dust", which enables zero fee and zero value outputs under certain conditions.
A replace dir is used as we modified
btcutilin this PR.