mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-05-23 23:36:56 +00:00

* '' * '' * '' * Changes genesis block version to 0. * a * a * All tests are done. * All tests passed for changed block version from int32 to uint16 * Adds validation of rejecting blocks with unknown versions. * Changes txn version from int32 to uint16. * . * Adds comments to exported functions. * Change functions name from ConvertFromRpcScriptPubKeyToRPCScriptPubKey to ConvertFromAppMsgRPCScriptPubKeyToRPCScriptPubKey and from ConvertFromRPCScriptPubKeyToRpcScriptPubKey to ConvertFromRPCScriptPubKeyToAppMsgRPCScriptPubKey * change comment to "ScriptPublicKey represents a Kaspad ScriptPublicKey" * delete part (tx.Version < 0) that cannot be exist on the if statement. * Revert protobuf version. * Fix a comment. * Fix a comment. * Rename a variable. * Rename a variable. * Remove a const. * Rename a type. * Rename a field. * Rename a field. * Remove commented-out code. * Remove dangerous nil case in DomainTransactionOutput.Clone(). * Remove a constant. * Fix a string. * Fix wrong totalScriptPubKeySize in transactionMassStandalonePart. * Remove a constant. * Remove an unused error. * Fix a serialization error. * Specify version types to be uint16 explicitly. * Use constants.ScriptPublicKeyVersion. * Fix a bad test. * Remove some whitespace. * Add a case to utxoEntry.Equal(). * Rename scriptPubKey to scriptPublicKey. * Remove a TODO. * Rename constants. * Rename a variable. * Add version to parseShortForm. Co-authored-by: tal <tal@daglabs.com> Co-authored-by: stasatdaglabs <stas@daglabs.com>
230 lines
7.8 KiB
Go
230 lines
7.8 KiB
Go
// Copyright (c) 2013-2015 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package txscript
|
|
|
|
import (
|
|
"github.com/kaspanet/go-secp256k1"
|
|
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/kaspanet/kaspad/domain/dagconfig"
|
|
"github.com/kaspanet/kaspad/util"
|
|
)
|
|
|
|
// RawTxInSignature returns the serialized Schnorr signature for the input idx of
|
|
// the given transaction, with hashType appended to it.
|
|
func RawTxInSignature(tx *externalapi.DomainTransaction, idx int, script *externalapi.ScriptPublicKey,
|
|
hashType SigHashType, key *secp256k1.SchnorrKeyPair) ([]byte, error) {
|
|
|
|
hash, err := CalcSignatureHash(script, hashType, tx, idx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
secpHash := secp256k1.Hash(*hash.ByteArray())
|
|
signature, err := key.SchnorrSign(&secpHash)
|
|
if err != nil {
|
|
return nil, errors.Errorf("cannot sign tx input: %s", err)
|
|
}
|
|
|
|
return append(signature.Serialize()[:], byte(hashType)), nil
|
|
}
|
|
|
|
// SignatureScript creates an input signature script for tx to spend KAS sent
|
|
// from a previous output to the owner of privKey. tx must include all
|
|
// transaction inputs and outputs, however txin scripts are allowed to be filled
|
|
// or empty. The returned script is calculated to be used as the idx'th txin
|
|
// sigscript for tx. script is the ScriptPublicKey of the previous output being used
|
|
// as the idx'th input. privKey is serialized in either a compressed or
|
|
// uncompressed format based on compress. This format must match the same format
|
|
// used to generate the payment address, or the script validation will fail.
|
|
func SignatureScript(tx *externalapi.DomainTransaction, idx int, script *externalapi.ScriptPublicKey, hashType SigHashType, privKey *secp256k1.SchnorrKeyPair) ([]byte, error) {
|
|
sig, err := RawTxInSignature(tx, idx, script, hashType, privKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pk, err := privKey.SchnorrPublicKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pkData, err := pk.Serialize()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return NewScriptBuilder().AddData(sig).AddData(pkData[:]).Script()
|
|
}
|
|
|
|
func sign(dagParams *dagconfig.Params, tx *externalapi.DomainTransaction, idx int,
|
|
script *externalapi.ScriptPublicKey, hashType SigHashType, kdb KeyDB, sdb ScriptDB) ([]byte,
|
|
ScriptClass, util.Address, error) {
|
|
|
|
class, address, err := ExtractScriptPubKeyAddress(script,
|
|
dagParams)
|
|
if err != nil {
|
|
return nil, NonStandardTy, nil, err
|
|
}
|
|
|
|
switch class {
|
|
case PubKeyHashTy:
|
|
// look up key for address
|
|
key, err := kdb.GetKey(address)
|
|
if err != nil {
|
|
return nil, class, nil, err
|
|
}
|
|
|
|
signedScript, err := SignatureScript(tx, idx, script, hashType, key)
|
|
if err != nil {
|
|
return nil, class, nil, err
|
|
}
|
|
|
|
return signedScript, class, address, nil
|
|
case ScriptHashTy:
|
|
script, err := sdb.GetScript(address)
|
|
if err != nil {
|
|
return nil, class, nil, err
|
|
}
|
|
|
|
return script, class, address, nil
|
|
default:
|
|
return nil, class, nil, errors.New("can't sign unknown transactions")
|
|
}
|
|
}
|
|
|
|
// mergeScripts merges sigScript and prevScript assuming they are both
|
|
// partial solutions for scriptPubKey spending output idx of tx. class, addresses
|
|
// and nrequired are the result of extracting the addresses from scriptPubKey.
|
|
// The return value is the best effort merging of the two scripts. Calling this
|
|
// function with addresses, class and nrequired that do not match scriptPubKey is
|
|
// an error and results in undefined behaviour.
|
|
func mergeScripts(dagParams *dagconfig.Params, tx *externalapi.DomainTransaction, idx int,
|
|
class ScriptClass, sigScript []byte, prevScript *externalapi.ScriptPublicKey) ([]byte, error) {
|
|
|
|
switch class {
|
|
case ScriptHashTy:
|
|
// Remove the last push in the script and then recurse.
|
|
// this could be a lot less inefficient.
|
|
sigPops, err := parseScript(sigScript)
|
|
if err != nil || len(sigPops) == 0 {
|
|
return prevScript.Script, nil
|
|
}
|
|
prevPops, err := parseScript(prevScript.Script)
|
|
if err != nil || len(prevPops) == 0 {
|
|
return sigScript, nil
|
|
}
|
|
|
|
// assume that script in sigPops is the correct one, we just
|
|
// made it.
|
|
script := sigPops[len(sigPops)-1].data
|
|
scriptPubKey := &externalapi.ScriptPublicKey{
|
|
Script: script,
|
|
Version: prevScript.Version,
|
|
}
|
|
// We already know this information somewhere up the stack.
|
|
class, _, _ :=
|
|
ExtractScriptPubKeyAddress(scriptPubKey, dagParams)
|
|
|
|
// regenerate scripts.
|
|
sigScript, _ := unparseScript(sigPops)
|
|
prevScriptByte, _ := unparseScript(prevPops)
|
|
prevScript = &externalapi.ScriptPublicKey{
|
|
Script: prevScriptByte,
|
|
Version: prevScript.Version,
|
|
}
|
|
// Merge
|
|
mergedScript, err := mergeScripts(dagParams, tx, idx, class, sigScript, prevScript)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Reappend the script and return the result.
|
|
builder := NewScriptBuilder()
|
|
builder.AddOps(mergedScript)
|
|
builder.AddData(script)
|
|
return builder.Script()
|
|
|
|
// It doesn't actually make sense to merge anything other than multiig
|
|
// and scripthash (because it could contain multisig). Everything else
|
|
// has either zero signature, can't be spent, or has a single signature
|
|
// which is either present or not. The other two cases are handled
|
|
// above. In the conflict case here we just assume the longest is
|
|
// correct (this matches behaviour of the reference implementation).
|
|
default:
|
|
if len(sigScript) > len(prevScript.Script) {
|
|
return sigScript, nil
|
|
}
|
|
return prevScript.Script, nil
|
|
}
|
|
}
|
|
|
|
// KeyDB is an interface type provided to SignTxOutput, it encapsulates
|
|
// any user state required to get the private keys for an address.
|
|
type KeyDB interface {
|
|
GetKey(util.Address) (*secp256k1.SchnorrKeyPair, error)
|
|
}
|
|
|
|
// KeyClosure implements KeyDB with a closure.
|
|
type KeyClosure func(util.Address) (*secp256k1.SchnorrKeyPair, error)
|
|
|
|
// GetKey implements KeyDB by returning the result of calling the closure.
|
|
func (kc KeyClosure) GetKey(address util.Address) (*secp256k1.SchnorrKeyPair, error) {
|
|
return kc(address)
|
|
}
|
|
|
|
// ScriptDB is an interface type provided to SignTxOutput, it encapsulates any
|
|
// user state required to get the scripts for an pay-to-script-hash address.
|
|
type ScriptDB interface {
|
|
GetScript(util.Address) ([]byte, error)
|
|
}
|
|
|
|
// ScriptClosure implements ScriptDB with a closure.
|
|
type ScriptClosure func(util.Address) ([]byte, error)
|
|
|
|
// GetScript implements ScriptDB by returning the result of calling the closure.
|
|
func (sc ScriptClosure) GetScript(address util.Address) ([]byte, error) {
|
|
return sc(address)
|
|
}
|
|
|
|
// SignTxOutput signs output idx of the given tx to resolve the script given in
|
|
// scriptPublicKey with a signature type of hashType. Any keys required will be
|
|
// looked up by calling getKey() with the string of the given address.
|
|
// Any pay-to-script-hash signatures will be similarly looked up by calling
|
|
// getScript. If previousScript is provided then the results in previousScript
|
|
// will be merged in a type-dependent manner with the newly generated.
|
|
// signature script.
|
|
func SignTxOutput(dagParams *dagconfig.Params, tx *externalapi.DomainTransaction, idx int,
|
|
scriptPublicKey *externalapi.ScriptPublicKey, hashType SigHashType, kdb KeyDB, sdb ScriptDB,
|
|
previousScript *externalapi.ScriptPublicKey) ([]byte, error) {
|
|
|
|
sigScript, class, _, err := sign(dagParams, tx,
|
|
idx, scriptPublicKey, hashType, kdb, sdb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if class == ScriptHashTy {
|
|
scriptHashPreimageScriptPublicKey := &externalapi.ScriptPublicKey{
|
|
Script: sigScript,
|
|
Version: scriptPublicKey.Version,
|
|
}
|
|
|
|
realSigScript, _, _, err := sign(dagParams, tx, idx,
|
|
scriptHashPreimageScriptPublicKey, hashType, kdb, sdb)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Append the p2sh script as the last push in the script.
|
|
builder := NewScriptBuilder()
|
|
builder.AddOps(realSigScript)
|
|
builder.AddData(sigScript)
|
|
|
|
sigScript, _ = builder.Script()
|
|
}
|
|
|
|
// Merge scripts. with any previous data, if any.
|
|
return mergeScripts(dagParams, tx, idx, class, sigScript, previousScript)
|
|
}
|