Update addresses to work with regtest network.

The prefix byte (netID) which is used to encode address is the same for
both the public test and regression test networks.  Previously the code
was working under the assumption there was a 1-to-1 mapping of prefix byte
to bitcoin network, however as noted above that assumption was not
correct.

This commit modifies things a bit to choose the prefix byte at address
creation time instead of at encode time and internally stores the prefix
byte instead of the network.  It also adds a new function, IsForNet, to the
Address interface which allows callers to test if an address is valid for
the passed network type.  The end result of this change is that callers
will only need to change their checks from testing if addr.Net() is the
active bitcoin network to instead using addr.IsForNet(activeNet).

Closes #2.
This commit is contained in:
Dave Collins 2014-02-26 13:51:49 -06:00
parent ca515e278d
commit e0ce788881
3 changed files with 148 additions and 94 deletions

View file

@ -26,7 +26,15 @@ var (
// checkBitcoinNet returns an error if the bitcoin network is not supported.
func checkBitcoinNet(net btcwire.BitcoinNet) error {
// Check for a valid bitcoin network.
if !(net == btcwire.MainNet || net == btcwire.TestNet3) {
switch net {
case btcwire.MainNet:
fallthrough
case btcwire.TestNet:
fallthrough
case btcwire.TestNet3:
return nil
default:
return ErrUnknownNet
}
@ -66,6 +74,10 @@ type Address interface {
// ScriptAddress returns the raw bytes of the address to be used
// when inserting the address into a txout's script.
ScriptAddress() []byte
// IsForNet returns whether or not the address is associated with the
// passed bitcoin network.
IsForNet(btcwire.BitcoinNet) bool
}
// DecodeAddr decodes the string encoding of an address and returns
@ -127,8 +139,8 @@ func DecodeAddr(addr string) (Address, error) {
// AddressPubKeyHash is an Address for a pay-to-pubkey-hash (P2PKH)
// transaction.
type AddressPubKeyHash struct {
hash [ripemd160.Size]byte
net btcwire.BitcoinNet
hash [ripemd160.Size]byte
netID byte
}
// NewAddressPubKeyHash returns a new AddressPubKeyHash. pkHash must
@ -144,7 +156,20 @@ func NewAddressPubKeyHash(pkHash []byte, net btcwire.BitcoinNet) (*AddressPubKey
return nil, err
}
addr := &AddressPubKeyHash{net: net}
// Choose the appropriate network ID for the address based on the
// network.
var netID byte
switch net {
case btcwire.MainNet:
netID = MainNetAddr
case btcwire.TestNet:
fallthrough
case btcwire.TestNet3:
netID = TestNetAddr
}
addr := &AddressPubKeyHash{netID: netID}
copy(addr.hash[:], pkHash)
return addr, nil
}
@ -152,15 +177,7 @@ func NewAddressPubKeyHash(pkHash []byte, net btcwire.BitcoinNet) (*AddressPubKey
// EncodeAddress returns the string encoding of a pay-to-pubkey-hash
// address. Part of the Address interface.
func (a *AddressPubKeyHash) EncodeAddress() string {
var netID byte
switch a.net {
case btcwire.MainNet:
netID = MainNetAddr
case btcwire.TestNet3:
netID = TestNetAddr
}
return encodeAddress(a.hash[:], netID)
return encodeAddress(a.hash[:], a.netID)
}
// ScriptAddress returns the bytes to be included in a txout script to pay
@ -169,10 +186,20 @@ func (a *AddressPubKeyHash) ScriptAddress() []byte {
return a.hash[:]
}
// Net returns the bitcoin network associated with the pay-to-pubkey-hash
// address.
func (a *AddressPubKeyHash) Net() btcwire.BitcoinNet {
return a.net
// IsForNet returns whether or not the pay-to-pubkey-hash address is associated
// with the passed bitcoin network.
func (a *AddressPubKeyHash) IsForNet(net btcwire.BitcoinNet) bool {
switch net {
case btcwire.MainNet:
return a.netID == MainNetAddr
case btcwire.TestNet:
fallthrough
case btcwire.TestNet3:
return a.netID == TestNetAddr
}
return false
}
// String returns a human-readable string for the pay-to-pubkey-hash address.
@ -185,8 +212,8 @@ func (a *AddressPubKeyHash) String() string {
// AddressScriptHash is an Address for a pay-to-script-hash (P2SH)
// transaction.
type AddressScriptHash struct {
hash [ripemd160.Size]byte
net btcwire.BitcoinNet
hash [ripemd160.Size]byte
netID byte
}
// NewAddressScriptHash returns a new AddressScriptHash. net must be
@ -211,7 +238,20 @@ func NewAddressScriptHashFromHash(scriptHash []byte, net btcwire.BitcoinNet) (*A
return nil, err
}
addr := &AddressScriptHash{net: net}
// Choose the appropriate network ID for the address based on the
// network.
var netID byte
switch net {
case btcwire.MainNet:
netID = MainNetScriptHash
case btcwire.TestNet:
fallthrough
case btcwire.TestNet3:
netID = TestNetScriptHash
}
addr := &AddressScriptHash{netID: netID}
copy(addr.hash[:], scriptHash)
return addr, nil
}
@ -219,15 +259,7 @@ func NewAddressScriptHashFromHash(scriptHash []byte, net btcwire.BitcoinNet) (*A
// EncodeAddress returns the string encoding of a pay-to-script-hash
// address. Part of the Address interface.
func (a *AddressScriptHash) EncodeAddress() string {
var netID byte
switch a.net {
case btcwire.MainNet:
netID = MainNetScriptHash
case btcwire.TestNet3:
netID = TestNetScriptHash
}
return encodeAddress(a.hash[:], netID)
return encodeAddress(a.hash[:], a.netID)
}
// ScriptAddress returns the bytes to be included in a txout script to pay
@ -236,10 +268,19 @@ func (a *AddressScriptHash) ScriptAddress() []byte {
return a.hash[:]
}
// Net returns the bitcoin network associated with the pay-to-script-hash
// address.
func (a *AddressScriptHash) Net() btcwire.BitcoinNet {
return a.net
// IsForNet returns whether or not the pay-to-script-hash address is associated
// with the passed bitcoin network.
func (a *AddressScriptHash) IsForNet(net btcwire.BitcoinNet) bool {
switch net {
case btcwire.MainNet:
return a.netID == MainNetScriptHash
case btcwire.TestNet:
fallthrough
case btcwire.TestNet3:
return a.netID == TestNetScriptHash
}
return false
}
// String returns a human-readable string for the pay-to-script-hash address.
@ -270,7 +311,7 @@ const (
type AddressPubKey struct {
pubKeyFormat PubKeyFormat
pubKey *btcec.PublicKey
net btcwire.BitcoinNet
netID byte
}
// NewAddressPubKey returns a new AddressPubKey which represents a pay-to-pubkey
@ -300,9 +341,30 @@ func NewAddressPubKey(serializedPubKey []byte, net btcwire.BitcoinNet) (*Address
pkFormat = PKFHybrid
}
// Check for a valid bitcoin network.
if err := checkBitcoinNet(net); err != nil {
return nil, err
}
// Choose the appropriate network ID for the address based on the
// network.
var netID byte
switch net {
case btcwire.MainNet:
netID = MainNetAddr
case btcwire.TestNet:
fallthrough
case btcwire.TestNet3:
netID = TestNetAddr
}
ecPubKey := (*btcec.PublicKey)(pubKey)
addr := &AddressPubKey{pubKeyFormat: pkFormat, pubKey: ecPubKey, net: net}
return addr, nil
return &AddressPubKey{
pubKeyFormat: pkFormat,
pubKey: ecPubKey,
netID: netID,
}, nil
}
// serialize returns the serialization of the public key according to the
@ -334,15 +396,7 @@ func (a *AddressPubKey) serialize() []byte {
//
// Part of the Address interface.
func (a *AddressPubKey) EncodeAddress() string {
var netID byte
switch a.net {
case btcwire.MainNet:
netID = MainNetAddr
case btcwire.TestNet3:
netID = TestNetAddr
}
return encodeAddress(Hash160(a.serialize()), netID)
return encodeAddress(Hash160(a.serialize()), a.netID)
}
// ScriptAddress returns the bytes to be included in a txout script to pay
@ -352,9 +406,20 @@ func (a *AddressPubKey) ScriptAddress() []byte {
return a.serialize()
}
// Net returns the bitcoin network associated with the pay-to-pubkey address.
func (a *AddressPubKey) Net() btcwire.BitcoinNet {
return a.net
// IsForNet returns whether or not the pay-to-pubkey address is associated
// with the passed bitcoin network.
func (a *AddressPubKey) IsForNet(net btcwire.BitcoinNet) bool {
switch net {
case btcwire.MainNet:
return a.netID == MainNetAddr
case btcwire.TestNet:
fallthrough
case btcwire.TestNet3:
return a.netID == TestNetAddr
}
return false
}
// String returns the hex-encoded human-readable string for the pay-to-pubkey
@ -382,8 +447,8 @@ func (a *AddressPubKey) SetFormat(pkFormat PubKeyFormat) {
// differs with the format. At the time of this writing, most Bitcoin addresses
// are pay-to-pubkey-hash constructed from the uncompressed public key.
func (a *AddressPubKey) AddressPubKeyHash() *AddressPubKeyHash {
// All potential error conditions are already checked, so it's safe to
// ignore the error here.
addr, _ := NewAddressPubKeyHash(Hash160(a.serialize()), a.net)
addr := &AddressPubKeyHash{netID: a.netID}
copy(addr.hash[:], Hash160(a.serialize()))
return addr
}

View file

@ -14,6 +14,9 @@ import (
"testing"
)
// invalidNet is an invalid bitcoin network.
const invalidNet = btcwire.BitcoinNet(0xffffffff)
func TestAddresses(t *testing.T) {
tests := []struct {
name string
@ -34,7 +37,7 @@ func TestAddresses(t *testing.T) {
[ripemd160.Size]byte{
0xe3, 0x4c, 0xce, 0x70, 0xc8, 0x63, 0x73, 0x27, 0x3e, 0xfc,
0xc5, 0x4c, 0xe7, 0xd2, 0xa4, 0x91, 0xbb, 0x4a, 0x0e, 0x84},
btcwire.MainNet),
btcutil.MainNetAddr),
f: func() (btcutil.Address, error) {
pkHash := []byte{
0xe3, 0x4c, 0xce, 0x70, 0xc8, 0x63, 0x73, 0x27, 0x3e, 0xfc,
@ -52,7 +55,7 @@ func TestAddresses(t *testing.T) {
[ripemd160.Size]byte{
0x0e, 0xf0, 0x30, 0x10, 0x7f, 0xd2, 0x6e, 0x0b, 0x6b, 0xf4,
0x05, 0x12, 0xbc, 0xa2, 0xce, 0xb1, 0xdd, 0x80, 0xad, 0xaa},
btcwire.MainNet),
btcutil.MainNetAddr),
f: func() (btcutil.Address, error) {
pkHash := []byte{
0x0e, 0xf0, 0x30, 0x10, 0x7f, 0xd2, 0x6e, 0x0b, 0x6b, 0xf4,
@ -70,7 +73,7 @@ func TestAddresses(t *testing.T) {
[ripemd160.Size]byte{
0x78, 0xb3, 0x16, 0xa0, 0x86, 0x47, 0xd5, 0xb7, 0x72, 0x83,
0xe5, 0x12, 0xd3, 0x60, 0x3f, 0x1f, 0x1c, 0x8d, 0xe6, 0x8f},
btcwire.TestNet3),
btcutil.TestNetAddr),
f: func() (btcutil.Address, error) {
pkHash := []byte{
0x78, 0xb3, 0x16, 0xa0, 0x86, 0x47, 0xd5, 0xb7, 0x72, 0x83,
@ -90,7 +93,7 @@ func TestAddresses(t *testing.T) {
pkHash := []byte{
0x78, 0xb3, 0x16, 0xa0, 0x86, 0x47, 0xd5, 0xb7, 0x72, 0x83,
0xe5, 0x12, 0xd3, 0x60, 0x3f, 0x1f, 0x1c, 0x8d, 0xe6, 0x8f}
return btcutil.NewAddressPubKeyHash(pkHash, btcwire.TestNet)
return btcutil.NewAddressPubKeyHash(pkHash, invalidNet)
},
},
{
@ -126,7 +129,7 @@ func TestAddresses(t *testing.T) {
[ripemd160.Size]byte{
0xf8, 0x15, 0xb0, 0x36, 0xd9, 0xbb, 0xbc, 0xe5, 0xe9, 0xf2,
0xa0, 0x0a, 0xbd, 0x1b, 0xf3, 0xdc, 0x91, 0xe9, 0x55, 0x10},
btcwire.MainNet),
btcutil.MainNetScriptHash),
f: func() (btcutil.Address, error) {
script := []byte{
0x52, 0x41, 0x04, 0x91, 0xbb, 0xa2, 0x51, 0x09, 0x12, 0xa5,
@ -166,7 +169,7 @@ func TestAddresses(t *testing.T) {
[ripemd160.Size]byte{
0xe8, 0xc3, 0x00, 0xc8, 0x79, 0x86, 0xef, 0xa8, 0x4c, 0x37,
0xc0, 0x51, 0x99, 0x29, 0x01, 0x9e, 0xf8, 0x6e, 0xb5, 0xb4},
btcwire.MainNet),
btcutil.MainNetScriptHash),
f: func() (btcutil.Address, error) {
hash := []byte{
0xe8, 0xc3, 0x00, 0xc8, 0x79, 0x86, 0xef, 0xa8, 0x4c, 0x37,
@ -185,7 +188,7 @@ func TestAddresses(t *testing.T) {
[ripemd160.Size]byte{
0xc5, 0x79, 0x34, 0x2c, 0x2c, 0x4c, 0x92, 0x20, 0x20, 0x5e,
0x2c, 0xdc, 0x28, 0x56, 0x17, 0x04, 0x0c, 0x92, 0x4a, 0x0a},
btcwire.TestNet3),
btcutil.TestNetScriptHash),
f: func() (btcutil.Address, error) {
hash := []byte{
0xc5, 0x79, 0x34, 0x2c, 0x2c, 0x4c, 0x92, 0x20, 0x20, 0x5e,
@ -218,7 +221,7 @@ func TestAddresses(t *testing.T) {
hash := []byte{
0xc5, 0x79, 0x34, 0x2c, 0x2c, 0x4c, 0x92, 0x20, 0x20, 0x5e,
0x2c, 0xdc, 0x28, 0x56, 0x17, 0x04, 0x0c, 0x92, 0x4a, 0x0a}
return btcutil.NewAddressScriptHashFromHash(hash, btcwire.TestNet)
return btcutil.NewAddressScriptHashFromHash(hash, invalidNet)
},
},
@ -234,7 +237,7 @@ func TestAddresses(t *testing.T) {
0x69, 0xc2, 0xe7, 0x79, 0x01, 0x57, 0x3d, 0x8d, 0x79, 0x03,
0xc3, 0xeb, 0xec, 0x3a, 0x95, 0x77, 0x24, 0x89, 0x5d, 0xca,
0x52, 0xc6, 0xb4},
btcutil.PKFCompressed, btcwire.MainNet),
btcutil.PKFCompressed, btcutil.MainNetAddr),
f: func() (btcutil.Address, error) {
serializedPubKey := []byte{
0x02, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95,
@ -256,7 +259,7 @@ func TestAddresses(t *testing.T) {
0xe9, 0x86, 0xe8, 0x84, 0x18, 0x5c, 0x61, 0xcf, 0x43, 0xe0,
0x01, 0xf9, 0x13, 0x7f, 0x23, 0xc2, 0xc4, 0x09, 0x27, 0x3e,
0xb1, 0x6e, 0x65},
btcutil.PKFCompressed, btcwire.MainNet),
btcutil.PKFCompressed, btcutil.MainNetAddr),
f: func() (btcutil.Address, error) {
serializedPubKey := []byte{
0x03, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1,
@ -281,7 +284,7 @@ func TestAddresses(t *testing.T) {
0xf9, 0x74, 0x44, 0x64, 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b,
0x8b, 0x64, 0xf9, 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43,
0xf6, 0x56, 0xb4, 0x12, 0xa3},
btcutil.PKFUncompressed, btcwire.MainNet),
btcutil.PKFUncompressed, btcutil.MainNetAddr),
f: func() (btcutil.Address, error) {
serializedPubKey := []byte{
0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, 0x6b,
@ -309,7 +312,7 @@ func TestAddresses(t *testing.T) {
0x96, 0x85, 0x26, 0x62, 0xce, 0x6a, 0x84, 0x7b, 0x19, 0x73,
0x76, 0x83, 0x01, 0x60, 0xc6, 0xd2, 0xeb, 0x5e, 0x6a, 0x4c,
0x44, 0xd3, 0x3f, 0x45, 0x3e},
btcutil.PKFHybrid, btcwire.MainNet),
btcutil.PKFHybrid, btcutil.MainNetAddr),
f: func() (btcutil.Address, error) {
serializedPubKey := []byte{
0x06, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95,
@ -337,7 +340,7 @@ func TestAddresses(t *testing.T) {
0x8a, 0x7e, 0xf8, 0xbd, 0x3b, 0x3c, 0xfb, 0x1e, 0xdb, 0x71,
0x17, 0xab, 0x65, 0x12, 0x9b, 0x8a, 0x2e, 0x68, 0x1f, 0x3c,
0x1e, 0x09, 0x08, 0xef, 0x7b},
btcutil.PKFHybrid, btcwire.MainNet),
btcutil.PKFHybrid, btcutil.MainNetAddr),
f: func() (btcutil.Address, error) {
serializedPubKey := []byte{
0x07, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1,
@ -362,7 +365,7 @@ func TestAddresses(t *testing.T) {
0x69, 0xc2, 0xe7, 0x79, 0x01, 0x57, 0x3d, 0x8d, 0x79, 0x03,
0xc3, 0xeb, 0xec, 0x3a, 0x95, 0x77, 0x24, 0x89, 0x5d, 0xca,
0x52, 0xc6, 0xb4},
btcutil.PKFCompressed, btcwire.TestNet3),
btcutil.PKFCompressed, btcutil.TestNetAddr),
f: func() (btcutil.Address, error) {
serializedPubKey := []byte{
0x02, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95,
@ -384,7 +387,7 @@ func TestAddresses(t *testing.T) {
0xe9, 0x86, 0xe8, 0x84, 0x18, 0x5c, 0x61, 0xcf, 0x43, 0xe0,
0x01, 0xf9, 0x13, 0x7f, 0x23, 0xc2, 0xc4, 0x09, 0x27, 0x3e,
0xb1, 0x6e, 0x65},
btcutil.PKFCompressed, btcwire.TestNet3),
btcutil.PKFCompressed, btcutil.TestNetAddr),
f: func() (btcutil.Address, error) {
serializedPubKey := []byte{
0x03, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1,
@ -409,7 +412,7 @@ func TestAddresses(t *testing.T) {
0xf9, 0x74, 0x44, 0x64, 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b,
0x8b, 0x64, 0xf9, 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43,
0xf6, 0x56, 0xb4, 0x12, 0xa3},
btcutil.PKFUncompressed, btcwire.TestNet3),
btcutil.PKFUncompressed, btcutil.TestNetAddr),
f: func() (btcutil.Address, error) {
serializedPubKey := []byte{
0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 0x01, 0x6b,
@ -437,7 +440,7 @@ func TestAddresses(t *testing.T) {
0x96, 0x85, 0x26, 0x62, 0xce, 0x6a, 0x84, 0x7b, 0x19, 0x73,
0x76, 0x83, 0x01, 0x60, 0xc6, 0xd2, 0xeb, 0x5e, 0x6a, 0x4c,
0x44, 0xd3, 0x3f, 0x45, 0x3e},
btcutil.PKFHybrid, btcwire.TestNet3),
btcutil.PKFHybrid, btcutil.TestNetAddr),
f: func() (btcutil.Address, error) {
serializedPubKey := []byte{
0x06, 0x19, 0x2d, 0x74, 0xd0, 0xcb, 0x94, 0x34, 0x4c, 0x95,
@ -465,7 +468,7 @@ func TestAddresses(t *testing.T) {
0x8a, 0x7e, 0xf8, 0xbd, 0x3b, 0x3c, 0xfb, 0x1e, 0xdb, 0x71,
0x17, 0xab, 0x65, 0x12, 0x9b, 0x8a, 0x2e, 0x68, 0x1f, 0x3c,
0x1e, 0x09, 0x08, 0xef, 0x7b},
btcutil.PKFHybrid, btcwire.TestNet3),
btcutil.PKFHybrid, btcutil.TestNetAddr),
f: func() (btcutil.Address, error) {
serializedPubKey := []byte{
0x07, 0xb0, 0xbd, 0x63, 0x42, 0x34, 0xab, 0xbb, 0x1b, 0xa1,
@ -514,30 +517,17 @@ func TestAddresses(t *testing.T) {
// Perform type-specific calculations.
var saddr []byte
var net btcwire.BitcoinNet
switch d := decoded.(type) {
case *btcutil.AddressPubKeyHash:
saddr = btcutil.TstAddressSAddr(encoded)
// Net is not part of the Address interface and
// must be calculated here.
net = d.Net()
case *btcutil.AddressScriptHash:
saddr = btcutil.TstAddressSAddr(encoded)
// Net is not part of the Address interface and
// must be calculated here.
net = d.Net()
case *btcutil.AddressPubKey:
// Ignore the error here since the script
// address is checked below.
saddr, _ = hex.DecodeString(d.String())
// Net is not part of the Address interface and
// must be calculated here.
net = d.Net()
}
// Check script address.
@ -549,7 +539,7 @@ func TestAddresses(t *testing.T) {
// Check networks. This check always succeeds for non-P2PKH and
// non-P2SH addresses as both nets will be Go's default zero value.
if net != test.net {
if !decoded.IsForNet(test.net) {
t.Errorf("%v: calculated network does not match expected",
test.name)
return

View file

@ -14,7 +14,6 @@ package btcutil
import (
"code.google.com/p/go.crypto/ripemd160"
"github.com/conformal/btcec"
"github.com/conformal/btcwire"
)
// SetBlockBytes sets the internal serialized block byte buffer to the passed
@ -31,37 +30,37 @@ func TstAppDataDir(goos, appName string, roaming bool) string {
}
// TstAddressPubKeyHash makes an AddressPubKeyHash, setting the
// unexported fields with the parameters hash and net.
// unexported fields with the parameters hash and netID.
func TstAddressPubKeyHash(hash [ripemd160.Size]byte,
net btcwire.BitcoinNet) *AddressPubKeyHash {
netID byte) *AddressPubKeyHash {
return &AddressPubKeyHash{
hash: hash,
net: net,
hash: hash,
netID: netID,
}
}
// TstAddressScriptHash makes an AddressScriptHash, setting the
// unexported fields with the parameters hash and net.
// unexported fields with the parameters hash and netID.
func TstAddressScriptHash(hash [ripemd160.Size]byte,
net btcwire.BitcoinNet) *AddressScriptHash {
netID byte) *AddressScriptHash {
return &AddressScriptHash{
hash: hash,
net: net,
hash: hash,
netID: netID,
}
}
// TstAddressPubKey makes an AddressPubKey, setting the unexported fields with
// the parameters.
func TstAddressPubKey(serializedPubKey []byte, pubKeyFormat PubKeyFormat,
net btcwire.BitcoinNet) *AddressPubKey {
netID byte) *AddressPubKey {
pubKey, _ := btcec.ParsePubKey(serializedPubKey, btcec.S256())
return &AddressPubKey{
pubKeyFormat: pubKeyFormat,
pubKey: (*btcec.PublicKey)(pubKey),
net: net,
netID: netID,
}
}