improve tx parser memory behavior
This is a non-functional change. Instead of `rawTransaction` (which carries the result of parsing a full transaction from zcashd) containing slices of _pointers_ to separately-allocated memory (one allocation for each spend, for example), allocate a slice of spends (one contiguous range of memory). A quick benchmark shows sync improves by about 8%.
This commit is contained in:
parent
ddf3781a60
commit
6d7d9f11a6
|
@ -19,14 +19,14 @@ type rawTransaction struct {
|
||||||
version uint32
|
version uint32
|
||||||
nVersionGroupID uint32
|
nVersionGroupID uint32
|
||||||
consensusBranchID uint32
|
consensusBranchID uint32
|
||||||
transparentInputs []*txIn
|
transparentInputs []txIn
|
||||||
transparentOutputs []*txOut
|
transparentOutputs []txOut
|
||||||
nLockTime uint32
|
nLockTime uint32
|
||||||
nExpiryHeight uint32
|
nExpiryHeight uint32
|
||||||
valueBalanceSapling int64
|
valueBalanceSapling int64
|
||||||
shieldedSpends []*spend
|
shieldedSpends []spend
|
||||||
shieldedOutputs []*output
|
shieldedOutputs []output
|
||||||
joinSplits []*joinSplit
|
joinSplits []joinSplit
|
||||||
joinSplitPubKey []byte
|
joinSplitPubKey []byte
|
||||||
joinSplitSig []byte
|
joinSplitSig []byte
|
||||||
bindingSigSapling []byte
|
bindingSigSapling []byte
|
||||||
|
@ -103,28 +103,26 @@ func (tx *Transaction) ParseTransparent(data []byte) ([]byte, error) {
|
||||||
// TODO: Duplicate/otherwise-too-many transactions are a possible DoS
|
// TODO: Duplicate/otherwise-too-many transactions are a possible DoS
|
||||||
// TODO: vector. At the moment we're assuming trusted input.
|
// TODO: vector. At the moment we're assuming trusted input.
|
||||||
// See https://nvd.nist.gov/vuln/detail/CVE-2018-17144 for an example.
|
// See https://nvd.nist.gov/vuln/detail/CVE-2018-17144 for an example.
|
||||||
tx.transparentInputs = make([]*txIn, txInCount)
|
tx.transparentInputs = make([]txIn, txInCount)
|
||||||
for i := 0; i < txInCount; i++ {
|
for i := 0; i < txInCount; i++ {
|
||||||
ti := &txIn{}
|
ti := &tx.transparentInputs[i]
|
||||||
s, err = ti.ParseFromSlice([]byte(s))
|
s, err = ti.ParseFromSlice([]byte(s))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "while parsing transparent input")
|
return nil, errors.Wrap(err, "while parsing transparent input")
|
||||||
}
|
}
|
||||||
tx.transparentInputs[i] = ti
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var txOutCount int
|
var txOutCount int
|
||||||
if !s.ReadCompactSize(&txOutCount) {
|
if !s.ReadCompactSize(&txOutCount) {
|
||||||
return nil, errors.New("could not read tx_out_count")
|
return nil, errors.New("could not read tx_out_count")
|
||||||
}
|
}
|
||||||
tx.transparentOutputs = make([]*txOut, txOutCount)
|
tx.transparentOutputs = make([]txOut, txOutCount)
|
||||||
for i := 0; i < txOutCount; i++ {
|
for i := 0; i < txOutCount; i++ {
|
||||||
to := &txOut{}
|
to := &tx.transparentOutputs[i]
|
||||||
s, err = to.ParseFromSlice([]byte(s))
|
s, err = to.ParseFromSlice([]byte(s))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "while parsing transparent output")
|
return nil, errors.Wrap(err, "while parsing transparent output")
|
||||||
}
|
}
|
||||||
tx.transparentOutputs[i] = to
|
|
||||||
}
|
}
|
||||||
return []byte(s), nil
|
return []byte(s), nil
|
||||||
}
|
}
|
||||||
|
@ -379,41 +377,38 @@ func (tx *Transaction) parseV4(data []byte) ([]byte, error) {
|
||||||
if !s.ReadCompactSize(&spendCount) {
|
if !s.ReadCompactSize(&spendCount) {
|
||||||
return nil, errors.New("could not read nShieldedSpend")
|
return nil, errors.New("could not read nShieldedSpend")
|
||||||
}
|
}
|
||||||
tx.shieldedSpends = make([]*spend, spendCount)
|
tx.shieldedSpends = make([]spend, spendCount)
|
||||||
for i := 0; i < spendCount; i++ {
|
for i := 0; i < spendCount; i++ {
|
||||||
newSpend := &spend{}
|
newSpend := &tx.shieldedSpends[i]
|
||||||
s, err = newSpend.ParseFromSlice([]byte(s), 4)
|
s, err = newSpend.ParseFromSlice([]byte(s), 4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "while parsing shielded Spend")
|
return nil, errors.Wrap(err, "while parsing shielded Spend")
|
||||||
}
|
}
|
||||||
tx.shieldedSpends[i] = newSpend
|
|
||||||
}
|
}
|
||||||
if !s.ReadCompactSize(&outputCount) {
|
if !s.ReadCompactSize(&outputCount) {
|
||||||
return nil, errors.New("could not read nShieldedOutput")
|
return nil, errors.New("could not read nShieldedOutput")
|
||||||
}
|
}
|
||||||
tx.shieldedOutputs = make([]*output, outputCount)
|
tx.shieldedOutputs = make([]output, outputCount)
|
||||||
for i := 0; i < outputCount; i++ {
|
for i := 0; i < outputCount; i++ {
|
||||||
newOutput := &output{}
|
newOutput := &tx.shieldedOutputs[i]
|
||||||
s, err = newOutput.ParseFromSlice([]byte(s), 4)
|
s, err = newOutput.ParseFromSlice([]byte(s), 4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "while parsing shielded Output")
|
return nil, errors.Wrap(err, "while parsing shielded Output")
|
||||||
}
|
}
|
||||||
tx.shieldedOutputs[i] = newOutput
|
|
||||||
}
|
}
|
||||||
var joinSplitCount int
|
var joinSplitCount int
|
||||||
if !s.ReadCompactSize(&joinSplitCount) {
|
if !s.ReadCompactSize(&joinSplitCount) {
|
||||||
return nil, errors.New("could not read nJoinSplit")
|
return nil, errors.New("could not read nJoinSplit")
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.joinSplits = make([]*joinSplit, joinSplitCount)
|
tx.joinSplits = make([]joinSplit, joinSplitCount)
|
||||||
if joinSplitCount > 0 {
|
if joinSplitCount > 0 {
|
||||||
for i := 0; i < joinSplitCount; i++ {
|
for i := 0; i < joinSplitCount; i++ {
|
||||||
js := &joinSplit{}
|
js := &tx.joinSplits[i]
|
||||||
s, err = js.ParseFromSlice([]byte(s))
|
s, err = js.ParseFromSlice([]byte(s))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "while parsing JoinSplit")
|
return nil, errors.Wrap(err, "while parsing JoinSplit")
|
||||||
}
|
}
|
||||||
tx.joinSplits[i] = js
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !s.ReadBytes(&tx.joinSplitPubKey, 32) {
|
if !s.ReadBytes(&tx.joinSplitPubKey, 32) {
|
||||||
|
@ -461,14 +456,13 @@ func (tx *Transaction) parseV5(data []byte) ([]byte, error) {
|
||||||
if spendCount >= (1 << 16) {
|
if spendCount >= (1 << 16) {
|
||||||
return nil, errors.New(fmt.Sprintf("spentCount (%d) must be less than 2^16", spendCount))
|
return nil, errors.New(fmt.Sprintf("spentCount (%d) must be less than 2^16", spendCount))
|
||||||
}
|
}
|
||||||
tx.shieldedSpends = make([]*spend, spendCount)
|
tx.shieldedSpends = make([]spend, spendCount)
|
||||||
for i := 0; i < spendCount; i++ {
|
for i := 0; i < spendCount; i++ {
|
||||||
newSpend := &spend{}
|
newSpend := &tx.shieldedSpends[i]
|
||||||
s, err = newSpend.ParseFromSlice([]byte(s), tx.version)
|
s, err = newSpend.ParseFromSlice([]byte(s), tx.version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "while parsing shielded Spend")
|
return nil, errors.Wrap(err, "while parsing shielded Spend")
|
||||||
}
|
}
|
||||||
tx.shieldedSpends[i] = newSpend
|
|
||||||
}
|
}
|
||||||
if !s.ReadCompactSize(&outputCount) {
|
if !s.ReadCompactSize(&outputCount) {
|
||||||
return nil, errors.New("could not read nShieldedOutput")
|
return nil, errors.New("could not read nShieldedOutput")
|
||||||
|
@ -476,14 +470,13 @@ func (tx *Transaction) parseV5(data []byte) ([]byte, error) {
|
||||||
if outputCount >= (1 << 16) {
|
if outputCount >= (1 << 16) {
|
||||||
return nil, errors.New(fmt.Sprintf("outputCount (%d) must be less than 2^16", outputCount))
|
return nil, errors.New(fmt.Sprintf("outputCount (%d) must be less than 2^16", outputCount))
|
||||||
}
|
}
|
||||||
tx.shieldedOutputs = make([]*output, outputCount)
|
tx.shieldedOutputs = make([]output, outputCount)
|
||||||
for i := 0; i < outputCount; i++ {
|
for i := 0; i < outputCount; i++ {
|
||||||
newOutput := &output{}
|
newOutput := &tx.shieldedOutputs[i]
|
||||||
s, err = newOutput.ParseFromSlice([]byte(s), tx.version)
|
s, err = newOutput.ParseFromSlice([]byte(s), tx.version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "while parsing shielded Output")
|
return nil, errors.Wrap(err, "while parsing shielded Output")
|
||||||
}
|
}
|
||||||
tx.shieldedOutputs[i] = newOutput
|
|
||||||
}
|
}
|
||||||
if spendCount+outputCount > 0 && !s.ReadInt64(&tx.valueBalanceSapling) {
|
if spendCount+outputCount > 0 && !s.ReadInt64(&tx.valueBalanceSapling) {
|
||||||
return nil, errors.New("could not read valueBalance")
|
return nil, errors.New("could not read valueBalance")
|
||||||
|
|
Loading…
Reference in New Issue