gecko/vms/secp256k1fx/fx.go

180 lines
4.7 KiB
Go

// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package secp256k1fx
import (
"errors"
"github.com/ava-labs/gecko/utils/crypto"
"github.com/ava-labs/gecko/utils/hashing"
"github.com/ava-labs/gecko/vms/components/verify"
)
var (
errWrongVMType = errors.New("wrong vm type")
errWrongTxType = errors.New("wrong tx type")
errWrongUTXOType = errors.New("wrong utxo type")
errWrongOutputType = errors.New("wrong output type")
errWrongInputType = errors.New("wrong input type")
errWrongCredentialType = errors.New("wrong credential type")
errWrongNumberOfOutputs = errors.New("wrong number of outputs for an operation")
errWrongNumberOfInputs = errors.New("wrong number of inputs for an operation")
errWrongNumberOfCredentials = errors.New("wrong number of credentials for an operation")
errWrongMintCreated = errors.New("wrong mint output created from the operation")
errWrongAmounts = errors.New("input is consuming a different amount than expected")
errTimelocked = errors.New("output is time locked")
errTooManySigners = errors.New("input has more signers than expected")
errTooFewSigners = errors.New("input has less signers than expected")
errInputCredentialSignersMismatch = errors.New("input expected a different number of signers than provided in the credential")
errWrongSigner = errors.New("credential does not produce expected signer")
)
// Fx ...
type Fx struct {
vm VM
secpFactory crypto.FactorySECP256K1R
}
// Initialize ...
func (fx *Fx) Initialize(vmIntf interface{}) error {
vm, ok := vmIntf.(VM)
if !ok {
return errWrongVMType
}
c := vm.Codec()
c.RegisterType(&MintOutput{})
c.RegisterType(&TransferOutput{})
c.RegisterType(&MintInput{})
c.RegisterType(&TransferInput{})
c.RegisterType(&Credential{})
fx.vm = vm
return nil
}
// VerifyOperation ...
func (fx *Fx) VerifyOperation(txIntf interface{}, utxosIntf, insIntf, credsIntf, outsIntf []interface{}) error {
tx, ok := txIntf.(Tx)
if !ok {
return errWrongTxType
}
if len(outsIntf) != 2 {
return errWrongNumberOfOutputs
}
if len(utxosIntf) != 1 || len(insIntf) != 1 {
return errWrongNumberOfInputs
}
if len(credsIntf) != 1 {
return errWrongNumberOfCredentials
}
utxo, ok := utxosIntf[0].(*MintOutput)
if !ok {
return errWrongUTXOType
}
in, ok := insIntf[0].(*MintInput)
if !ok {
return errWrongInputType
}
cred, ok := credsIntf[0].(*Credential)
if !ok {
return errWrongCredentialType
}
newMint, ok := outsIntf[0].(*MintOutput)
if !ok {
return errWrongOutputType
}
newOutput, ok := outsIntf[1].(*TransferOutput)
if !ok {
return errWrongOutputType
}
return fx.verifyOperation(tx, utxo, in, cred, newMint, newOutput)
}
func (fx *Fx) verifyOperation(tx Tx, utxo *MintOutput, in *MintInput, cred *Credential, newMint *MintOutput, newOutput *TransferOutput) error {
if err := verify.All(utxo, in, cred, newMint, newOutput); err != nil {
return err
}
if !utxo.Equals(&newMint.OutputOwners) {
return errWrongMintCreated
}
return fx.verifyCredentials(tx, &utxo.OutputOwners, &in.Input, cred)
}
// VerifyTransfer ...
func (fx *Fx) VerifyTransfer(txIntf, utxoIntf, inIntf, credIntf interface{}) error {
tx, ok := txIntf.(Tx)
if !ok {
return errWrongTxType
}
utxo, ok := utxoIntf.(*TransferOutput)
if !ok {
return errWrongUTXOType
}
in, ok := inIntf.(*TransferInput)
if !ok {
return errWrongInputType
}
cred, ok := credIntf.(*Credential)
if !ok {
return errWrongCredentialType
}
return fx.verifyTransfer(tx, utxo, in, cred)
}
func (fx *Fx) verifyTransfer(tx Tx, utxo *TransferOutput, in *TransferInput, cred *Credential) error {
if err := verify.All(utxo, in, cred); err != nil {
return err
}
clock := fx.vm.Clock()
switch {
case utxo.Amt != in.Amt:
return errWrongAmounts
case utxo.Locktime > clock.Unix():
return errTimelocked
}
return fx.verifyCredentials(tx, &utxo.OutputOwners, &in.Input, cred)
}
func (fx *Fx) verifyCredentials(tx Tx, out *OutputOwners, in *Input, cred *Credential) error {
numSigs := len(in.SigIndices)
switch {
case out.Threshold < uint32(numSigs):
return errTooManySigners
case out.Threshold > uint32(numSigs):
return errTooFewSigners
case numSigs != len(cred.Sigs):
return errInputCredentialSignersMismatch
}
txBytes := tx.UnsignedBytes()
txHash := hashing.ComputeHash256(txBytes)
for i, index := range in.SigIndices {
sig := cred.Sigs[i]
pk, err := fx.secpFactory.RecoverHashPublicKey(txHash, sig[:])
if err != nil {
return err
}
expectedAddress := out.Addrs[index]
if !expectedAddress.Equals(pk.Address()) {
return errWrongSigner
}
}
return nil
}