[9491455]比特币的地址类型

比特币的地址类型

这部分内容主要来自于btcutil/address.go

一直困惑比特币是如何验证交易的,看了这个地质类型算是有点豁然开朗,实际上比特币的交易验证规则还是有点复杂的,它并不像以太坊那么简单明确.

个人理解,比特币对于交易的处理,首先是根据 pubkey script 判断是什么地址类型,然后进行不同的验证方法. 比如如果地质类型是AddressWitnessPubKeyHash,那么验证规则就明显和 P2PKH 不一样.

以下是address.go 中如何解析出地址:

// DecodeAddress decodes the string encoding of an address and returns
// the Address if addr is a valid encoding for a known address type.
//
// The bitcoin network the address is associated with is extracted if possible.
// When the address does not encode the network, such as in the case of a raw
// public key, the address will be associated with the passed defaultNet.
func DecodeAddress(addr string, defaultNet *chaincfg.Params) (Address, error) {
	// Bech32 encoded segwit addresses start with a human-readable part
	// (hrp) followed by '1'. For Bitcoin mainnet the hrp is "bc", and for
	// testnet it is "tb". If the address string has a prefix that matches
	// one of the prefixes for the known networks, we try to decode it as
	// a segwit address.
	oneIndex := strings.LastIndexByte(addr, '1')
	if oneIndex > 1 {
		// The HRP is everything before the found '1'.
		hrp := strings.ToLower(addr[:oneIndex])
		if hrp == defaultNet.Bech32HRPSegwit {
			witnessVer, witnessProg, err := decodeSegWitAddress(addr)
			if err != nil {
				return nil, err
			}

			// We currently only support P2WPKH and P2WSH, which is
			// witness version 0.
			if witnessVer != 0 {
				return nil, UnsupportedWitnessVerError(witnessVer)
			}

			switch len(witnessProg) {
			case 20:
				return newAddressWitnessPubKeyHash(hrp, witnessProg)
			case 32:
				return newAddressWitnessScriptHash(hrp, witnessProg)
			default:
				return nil, UnsupportedWitnessProgLenError(len(witnessProg))
			}
		}
	}

	// Serialized public keys are either 65 bytes (130 hex chars) if
	// uncompressed/hybrid or 33 bytes (66 hex chars) if compressed.
	if len(addr) == 130 || len(addr) == 66 {
		serializedPubKey, err := hex.DecodeString(addr)
		if err != nil {
			return nil, err
		}
		return NewAddressPubKey(serializedPubKey, defaultNet)
	}

	// Switch on decoded length to determine the type.
	decoded, netID, err := base58.CheckDecode(addr)
	if err != nil {
		if err == base58.ErrChecksum {
			return nil, ErrChecksumMismatch
		}
		return nil, errors.New("decoded address is of unknown format")
	}
	switch len(decoded) {
	case ripemd160.Size: // P2PKH or P2SH
		isP2PKH := netID == defaultNet.PubKeyHashAddrID
		isP2SH := netID == defaultNet.ScriptHashAddrID
		switch hash160 := decoded; {
		case isP2PKH && isP2SH:
			return nil, ErrAddressCollision
		case isP2PKH:
			return newAddressPubKeyHash(hash160, netID)
		case isP2SH:
			return newAddressScriptHashFromHash(hash160, netID)
		default:
			return nil, ErrUnknownAddressType
		}

	default:
		return nil, errors.New("decoded address is of unknown size")
	}
}

总共有四中地质类型:

AddressPubKey

// AddressPubKey is an Address for a pay-to-pubkey transaction.
type AddressPubKey struct {
	pubKeyFormat PubKeyFormat
	pubKey       *btcec.PublicKey
	pubKeyHashID byte
}

AddressPubKeyHash

// AddressPubKeyHash is an Address for a pay-to-pubkey-hash (P2PKH)
// transaction.
type AddressPubKeyHash struct {
	hash  [ripemd160.Size]byte
	netID byte
}

AddressScriptHash

// AddressScriptHash is an Address for a pay-to-script-hash (P2SH)
// transaction.
type AddressScriptHash struct {
	hash  [ripemd160.Size]byte
	netID byte
}

AddressWitnessPubKeyHash

// AddressWitnessPubKeyHash is an Address for a pay-to-witness-pubkey-hash
// (P2WPKH) output. See BIP 173 for further details regarding native segregated
// witness address encoding:
// https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
type AddressWitnessPubKeyHash struct {
	hrp            string
	witnessVersion byte
	witnessProgram [20]byte
}

AddressWitnessScriptHash

// AddressWitnessScriptHash is an Address for a pay-to-witness-script-hash
// (P2WSH) output. See BIP 173 for further details regarding native segregated
// witness address encoding:
// https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
type AddressWitnessScriptHash struct {
	hrp            string
	witnessVersion byte
	witnessProgram [32]byte
}

地址类型对应的实际上就是锁定脚本的类型

比特币种的脚本执行流程比较复杂,必须根据脚本的内容来决定如何执行.
这与以太坊是完全不一样的,以太坊中是把输入环境构造完毕,不关心执行的code是什么.

PayToPubKeyHash

 OP_DUP OP_HASH160 128004ff2fcaf13b2b91eb654b1dc2b674f7ec61 OP_EQUALVERIFY OP_CHECKSIG

判断是否是PayToPubKey的代码

// isPubkeyHash returns true if the script passed is a pay-to-pubkey-hash
// transaction, false otherwise.
func isPubkeyHash(pops []parsedOpcode) bool {
	return len(pops) == 5 &&
		pops[0].opcode.value == OP_DUP &&
		pops[1].opcode.value == OP_HASH160 &&
		pops[2].opcode.value == OP_DATA_20 &&
		pops[3].opcode.value == OP_EQUALVERIFY &&
		pops[4].opcode.value == OP_CHECKSIG

}

PayToPubKey

pubkey是压缩的

DATA_33 0x028004ff2fcaf13b2b91eb654b1dc2b674f7ec61128004ff2fcaf13b2b91eb654a  OP_CHECKSIG

pubkey是非压缩的

DATA_65 0x0411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3 CHECKSIG
// isPubkey returns true if the script passed is a pay-to-pubkey transaction,
// false otherwise.
func isPubkey(pops []parsedOpcode) bool {
	// Valid pubkeys are either 33 or 65 bytes.
	return len(pops) == 2 &&
		(len(pops[0].data) == 33 || len(pops[0].data) == 65) &&
		pops[1].opcode.value == OP_CHECKSIG
}

PayToScriptHash

HASH160 DATA_20 0xb3a84b564602a9d68b4c9f19c2ea61458ff7826c EQUAL
// isScriptHash returns true if the script passed is a pay-to-script-hash
// transaction, false otherwise.
func isScriptHash(pops []parsedOpcode) bool {
	return len(pops) == 3 &&
		pops[0].opcode.value == OP_HASH160 &&
		pops[1].opcode.value == OP_DATA_20 &&
		pops[2].opcode.value == OP_EQUAL
}

PayToWitnessPubKeyHash

为了区分,强制增加了一个无用的OP_O,如果按照前面的三种方式去执行,就是这个Tx输出所有人都可以花费,不需要提供任何证明.因此是所谓的软分叉,提供了向下兼容.
其执行过程:

  1. 移除 OP_0,这是版本信息
  2. 将剩下的DATA_20 和0x365ab47888e150ff46f8d51bce36dcd680f1283f 组装成P2PKH
    OP_DUP OP_HASH160 365ab47888e150ff46f8d51bce36dcd680f1283f OP_EQUALVERIFY OP_CHECKSIG
  3. 将Tx中的Witness依次压栈,然后开始执行,验证方式如同P2PKH
    代码见txscript/engine.go的verifyWitnessProgram
    OP_0 DATA_20 0x365ab47888e150ff46f8d51bce36dcd680f1283f
    go // isWitnessPubKeyHash returns true if the passed script is a // pay-to-witness-pubkey-hash, and false otherwise. func isWitnessPubKeyHash(pops []parsedOpcode) bool { return len(pops) == 2 && pops[0].opcode.value == OP_0 && pops[1].opcode.value == OP_DATA_20 }

PayToWitnessScriptHash

其执行过程如下:

  1. 移除0,作为版本信息
  2. 验证Tx中Witness数组的最后一个的Hash值和0xe112b88a0cd87ba387f449d443ee2596eb353beb1f0351ab2cba8909d875db23是否相等
  3. 验证过以后,将Witness数组的最后一个作为锁定脚本
  4. 将Witness其他0到n-2元素作为解锁脚本依次压栈
  5. 如同P2SH一样执行验证
    代码见txscript/engine.go的verifyWitnessProgram
    0 DATA_32 0xe112b88a0cd87ba387f449d443ee2596eb353beb1f0351ab2cba8909d875db23

// isWitnessScriptHash returns true if the passed script is a
// pay-to-witness-script-hash transaction, false otherwise.
func isWitnessScriptHash(pops []parsedOpcode) bool {
	return len(pops) == 2 &&
		pops[0].opcode.value == OP_0 &&
		pops[1].opcode.value == OP_DATA_32
}