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

[Chore] Collection of backports #388

Merged
merged 7 commits into from
Jul 22, 2020
Merged
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
22 changes: 11 additions & 11 deletions bchec/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,24 +212,24 @@ func (f *fieldVal) SetBytes(b *[32]byte) *fieldVal {
return f
}

// SetByteSlice packs the passed big-endian value into the internal field value
// representation. Only the first 32-bytes are used. As a result, it is up to
// the caller to ensure numbers of the appropriate size are used or the value
// will be truncated.
// SetByteSlice interprets the provided slice as a 256-bit big-endian unsigned
// integer (meaning it is truncated to the first 32 bytes), packs it into the
// internal field value representation, and returns the updated field value.
//
// Note that since passing a slice with more than 32 bytes is truncated, it is
// possible that the truncated value is less than the field prime. It is up to
// the caller to decide whether it needs to provide numbers of the appropriate
// size or if it is acceptable to use this function with the described
// truncation behavior.
//
// The field value is returned to support chaining. This enables syntax like:
// f := new(fieldVal).SetByteSlice(byteSlice)
func (f *fieldVal) SetByteSlice(b []byte) *fieldVal {
var b32 [32]byte
if len(b) > 32 {
b = b[:32]
}

var b32 [32]byte
for i := 0; i < len(b); i++ {
if i < 32 {
b32[i+(32-len(b))] = b[i]
}
}
copy(b32[32-len(b):], b)
return f.SetBytes(&b32)
}

Expand Down
154 changes: 154 additions & 0 deletions bchec/field_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package bchec

import (
"encoding/hex"
"reflect"
"testing"
)
Expand Down Expand Up @@ -820,3 +821,156 @@ func TestInverse(t *testing.T) {
}
}
}

// TestFieldSetBytes ensures that setting a field value to a 256-bit big-endian
// unsigned integer via both the slice and array methods works as expected for
// edge cases. Random cases are tested via the various other tests.
func TestFieldSetBytes(t *testing.T) {
tests := []struct {
name string // test description
in string // hex encoded test value
expected [10]uint32 // expected raw ints
}{{
name: "zero",
in: "00",
expected: [10]uint32{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
}, {
name: "field prime",
in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f",
expected: [10]uint32{
0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff,
0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff,
},
}, {
name: "field prime - 1",
in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2e",
expected: [10]uint32{
0x03fffc2e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff,
0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff,
},
}, {
name: "field prime + 1 (overflow in word zero)",
in: "fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc30",
expected: [10]uint32{
0x03fffc30, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff,
0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff,
},
}, {
name: "field prime first 32 bits",
in: "fffffc2f",
expected: [10]uint32{
0x03fffc2f, 0x00000003f, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
},
}, {
name: "field prime word zero",
in: "03fffc2f",
expected: [10]uint32{
0x03fffc2f, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
},
}, {
name: "field prime first 64 bits",
in: "fffffffefffffc2f",
expected: [10]uint32{
0x03fffc2f, 0x03ffffbf, 0x00000fff, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
},
}, {
name: "field prime word zero and one",
in: "0ffffefffffc2f",
expected: [10]uint32{
0x03fffc2f, 0x03ffffbf, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
},
}, {
name: "field prime first 96 bits",
in: "fffffffffffffffefffffc2f",
expected: [10]uint32{
0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x0003ffff, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
},
}, {
name: "field prime word zero, one, and two",
in: "3ffffffffffefffffc2f",
expected: [10]uint32{
0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
},
}, {
name: "overflow in word one (prime + 1<<26)",
in: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffff03fffc2f",
expected: [10]uint32{
0x03fffc2f, 0x03ffffc0, 0x03ffffff, 0x03ffffff, 0x03ffffff,
0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff,
},
}, {
name: "(field prime - 1) * 2 NOT mod P, truncated >32 bytes",
in: "01fffffffffffffffffffffffffffffffffffffffffffffffffffffffdfffff85c",
expected: [10]uint32{
0x01fffff8, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff,
0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x00007fff,
},
}, {
name: "2^256 - 1",
in: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
expected: [10]uint32{
0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff,
0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff,
},
}, {
name: "alternating bits",
in: "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5",
expected: [10]uint32{
0x01a5a5a5, 0x01696969, 0x025a5a5a, 0x02969696, 0x01a5a5a5,
0x01696969, 0x025a5a5a, 0x02969696, 0x01a5a5a5, 0x00296969,
},
}, {
name: "alternating bits 2",
in: "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a",
expected: [10]uint32{
0x025a5a5a, 0x02969696, 0x01a5a5a5, 0x01696969, 0x025a5a5a,
0x02969696, 0x01a5a5a5, 0x01696969, 0x025a5a5a, 0x00169696,
},
}}

for _, test := range tests {
inBytes := hexToBytes(test.in)

// Ensure setting the bytes via the slice method works as expected.
var f fieldVal
f.SetByteSlice(inBytes)
if !reflect.DeepEqual(f.n, test.expected) {
t.Errorf("%s: unexpected result\ngot: %x\nwant: %x", test.name, f.n,
test.expected)
continue
}

// Ensure setting the bytes via the array method works as expected.
var f2 fieldVal
var b32 [32]byte
truncatedInBytes := inBytes
if len(truncatedInBytes) > 32 {
truncatedInBytes = truncatedInBytes[:32]
}
copy(b32[32-len(truncatedInBytes):], truncatedInBytes)
f2.SetBytes(&b32)
if !reflect.DeepEqual(f2.n, test.expected) {
t.Errorf("%s: unexpected result\ngot: %x\nwant: %x", test.name,
f2.n, test.expected)
continue
}
}
}

// hexToBytes converts the passed hex string into bytes and will panic if there
// is an error. This is only provided for the hard-coded constants so errors in
// the source code can be detected. It will only (and must only) be called with
// hard-coded values.
func hexToBytes(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex in source file: " + s)
}
return b
}
13 changes: 2 additions & 11 deletions blockchain/chainio.go
Original file line number Diff line number Diff line change
Expand Up @@ -1327,18 +1327,9 @@ func (b *BlockChain) initChainState(fastSync bool) error {

blockIndexBucket := dbTx.Metadata().Bucket(blockIndexBucketName)

// Determine how many blocks will be loaded into the index so we can
// allocate the right amount.
var blockCount int32
cursor := blockIndexBucket.Cursor()
for ok := cursor.First(); ok; ok = cursor.Next() {
blockCount++
}
blockNodes := make([]blockNode, blockCount)

var i int32
var lastNode *blockNode
cursor = blockIndexBucket.Cursor()
cursor := blockIndexBucket.Cursor()
for ok := cursor.First(); ok; ok = cursor.Next() {
header, status, err := deserializeBlockRow(cursor.Value())
if err != nil {
Expand Down Expand Up @@ -1371,7 +1362,7 @@ func (b *BlockChain) initChainState(fastSync bool) error {

// Initialize the block node for the block, connect it,
// and add it to the block index.
node := &blockNodes[i]
node := new(blockNode)
initBlockNode(node, header, parent)
node.status = status
b.index.addNode(node)
Expand Down
18 changes: 17 additions & 1 deletion blockchain/utxocache.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ package blockchain
import (
"container/list"
"fmt"
"github.com/gcash/bchd/txscript"
"sync"

"github.com/gcash/bchd/txscript"

"github.com/gcash/bchd/chaincfg/chainhash"
"github.com/gcash/bchd/database"
"github.com/gcash/bchd/wire"
Expand Down Expand Up @@ -79,6 +80,21 @@ type UtxoEntry struct {
packedFlags txoFlags
}

// NewUtxoEntry returns a new UtxoEntry built from the arguments.
func NewUtxoEntry(txOut *wire.TxOut, blockHeight int32, isCoinbase bool) *UtxoEntry {
var cbFlag txoFlags
if isCoinbase {
cbFlag |= tfCoinBase
}

return &UtxoEntry{
amount: txOut.Value,
pkScript: txOut.PkScript,
blockHeight: blockHeight,
packedFlags: cbFlag,
}
}

// IsCoinBase returns whether or not the output was contained in a coinbase
// transaction.
func (entry *UtxoEntry) IsCoinBase() bool {
Expand Down
6 changes: 6 additions & 0 deletions btcjson/chainsvrcmds.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ type CreateRawTransactionCmd struct {
func NewCreateRawTransactionCmd(inputs []TransactionInput, amounts map[string]float64,
lockTime *int64) *CreateRawTransactionCmd {

// To make sure we're serializing this to the empty list and not null, we
// explicitly initialize the list
if inputs == nil {
inputs = []TransactionInput{}
}

return &CreateRawTransactionCmd{
Inputs: inputs,
Amounts: amounts,
Expand Down
15 changes: 15 additions & 0 deletions btcjson/chainsvrcmds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ func TestChainSvrCmds(t *testing.T) {
Amounts: map[string]float64{"456": .0123},
},
},
{
name: "createrawtransaction - no inputs",
newCmd: func() (interface{}, error) {
return btcjson.NewCmd("createrawtransaction", `[]`, `{"456":0.0123}`)
},
staticCmd: func() interface{} {
amounts := map[string]float64{"456": .0123}
return btcjson.NewCreateRawTransactionCmd(nil, amounts, nil)
},
marshalled: `{"jsonrpc":"1.0","method":"createrawtransaction","params":[[],{"456":0.0123}],"id":1}`,
unmarshalled: &btcjson.CreateRawTransactionCmd{
Inputs: []btcjson.TransactionInput{},
Amounts: map[string]float64{"456": .0123},
},
},
{
name: "createrawtransaction optional",
newCmd: func() (interface{}, error) {
Expand Down
1 change: 1 addition & 0 deletions btcjson/jsonrpcerr.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const (
ErrRPCDatabase RPCErrorCode = -20
ErrRPCDeserialization RPCErrorCode = -22
ErrRPCVerify RPCErrorCode = -25
ErrRPCInWarmup RPCErrorCode = -28
)

// Peer-to-peer client errors.
Expand Down
2 changes: 1 addition & 1 deletion connmgr/dynamicbanscore.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ type DynamicBanScore struct {
func (s *DynamicBanScore) String() string {
s.mtx.Lock()
r := fmt.Sprintf("persistent %v + transient %v at %v = %v as of now",
s.persistent, s.transient, s.lastUnix, s.Int())
s.persistent, s.transient, s.lastUnix, s.int(time.Now()))
s.mtx.Unlock()
return r
}
Expand Down
12 changes: 12 additions & 0 deletions connmgr/dynamicbanscore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,15 @@ func TestDynamicBanScoreReset(t *testing.T) {
t.Errorf("Failed to reset ban score.")
}
}

// TestDynamicBanScoreString
func TestDynamicBanScoreString(t *testing.T) {
var bs DynamicBanScore
base := time.Now()

r := bs.increase(100, 50, base)
if r != 150 {
t.Errorf("Unexpected result %d after ban score increase.", r)
}
t.Log(bs.String())
}
2 changes: 1 addition & 1 deletion database/internal/treap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ treap
Package treap implements a treap data structure that is used to hold ordered
key/value pairs using a combination of binary search tree and heap semantics.
It is a self-organizing and randomized data structure that doesn't require
complex operations to to maintain balance. Search, insert, and delete
complex operations to maintain balance. Search, insert, and delete
operations are all O(log n). Both mutable and immutable variants are provided.

The mutable variant is typically faster since it is able to simply update the
Expand Down
3 changes: 2 additions & 1 deletion rpcclient/rawtransactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,8 @@ func (c *Client) CreateRawTransactionAsync(inputs []btcjson.TransactionInput,
}

// CreateRawTransaction returns a new transaction spending the provided inputs
// and sending to the provided addresses.
// and sending to the provided addresses. If the inputs are either nil or an
// empty slice, it is interpreted as an empty slice.
func (c *Client) CreateRawTransaction(inputs []btcjson.TransactionInput,
amounts map[bchutil.Address]bchutil.Amount, lockTime *int64) (*wire.MsgTx, error) {

Expand Down