IntersectMBO / cardano-node

The core component that is used to participate in a Cardano decentralised blockchain.
https://cardano.org
Apache License 2.0
3.08k stars 720 forks source link

[FEATURE] - cardano multisig wallet with staking #5917

Closed ekko-Huang closed 4 months ago

ekko-Huang commented 4 months ago

hey guys. I want to send a registration transaction for a multi-signature wallet, but I don't know how to organize NativeScript and signatures.

import (
    "encoding/hex"
    "fmt"
    "log"
    "testing"

    "github.com/btcsuite/btcutil/bech32"
    "github.com/echovl/cardano-go"
    "github.com/echovl/cardano-go/crypto"
    "github.com/fivebinaries/go-cardano-serialization/address"
    "github.com/fivebinaries/go-cardano-serialization/bip32"
    "github.com/fivebinaries/go-cardano-serialization/network"
    "github.com/tyler-smith/go-bip39"
)

func TestMultiSignDelegationTx(t *testing.T) {
    //m1 := "canyon soft bitter into mixed find cover method junk glide place rabbit"
    m1 := "where visa pizza bronze tumble indicate loud endorse slice secret hip fix"
    m2 := "stem mandate sand lecture hip ancient issue happy office about miracle bounce"
    m3 := "shallow remember arrest magnet resist aspect equip trash season sell bless clown"
    p1 := getPaymentPrvKey(getRootKey(m1))
    payment1 := p1.Public()
    payment1Hash := payment1.PublicKey().Hash()

    s1 := getStakePrvKey(getRootKey(m1))
    stake1 := s1.Public()
    stake1Hash := stake1.PublicKey().Hash()

    p2 := getPaymentPrvKey(getRootKey(m2))
    payment2 := p2.Public()
    payment2Hash := payment2.PublicKey().Hash()

    s2 := getStakePrvKey(getRootKey(m2))
    stake2 := s2.Public()
    stake2Hash := stake2.PublicKey().Hash()

    p3 := getPaymentPrvKey(getRootKey(m3))
    payment3 := p3.Public()
    payment3Hash := payment3.PublicKey().Hash()

    s3 := getStakePrvKey(getRootKey(m3))
    stake3 := s3.Public()
    stake3Hash := stake3.PublicKey().Hash()

    n := cardano.NativeScript{
       Type: ScriptAtLeast, // Require-Of-M
       N:    2,
       Scripts: []cardano.NativeScript{
          {
             Type:    ScriptPubKey,
             KeyHash: payment1Hash[:],
          },
          {
             Type:    ScriptPubKey,
             KeyHash: payment2Hash[:],
          },
          {
             Type:    ScriptPubKey,
             KeyHash: payment3Hash[:],
          },
       },
    }
    paymentScriptHash, err := n.Hash()
    if err != nil {
       panic(err)
    }

    s := cardano.NativeScript{
       Type: ScriptAtLeast, // Require-Of-M
       N:    2,
       Scripts: []cardano.NativeScript{
          {
             Type:    ScriptPubKey,
             KeyHash: stake1Hash[:],
          },
          {
             Type:    ScriptPubKey,
             KeyHash: stake2Hash[:],
          },
          {
             Type:    ScriptPubKey,
             KeyHash: stake3Hash[:],
          },
       },
    }
    stakeScriptHash, err := s.Hash()
    if err != nil {
       panic(err)
    }

    multisign := address.NewBaseAddress(
       network.TestNet(),
       &address.StakeCredential{
          Kind:    address.ScriptStakeCredentialType,
          Payload: paymentScriptHash,
       },
       &address.StakeCredential{
          Kind:    address.ScriptStakeCredentialType,
          Payload: stakeScriptHash,
       },
    )

    fmt.Println("multiSign Address:", multisign.String())
    fmt.Println("stake Address:", multisign.ToReward().String())
    fmt.Println("stakeHash", hex.EncodeToString(stakeScriptHash))

    txBuilder := cardano.NewTxBuilder(alonzoProtocol)
    stakeByte, _ := s.Bytes()
    stakeByte = append([]byte{byte(0)}, stakeByte...)
    // create RegistrationCert
    stakeDelCert, err := cardano.NewStakeRegistrationCertificate(stakeByte)
    if err != nil {
       panic(err)
    }
    rAddr, _ := cardano.NewAddress(multisign.String())
    txInputHash, _ := cardano.NewHash32("e55ae4a146ff4779fc88b772102fae92205b637db41394d53b16d33b658c2c44")
    txInputAmount := cardano.NewValue(20000000)

    txBuilder.AddInputs(cardano.NewTxInput(txInputHash, 0, txInputAmount))
    txBuilder.AddOutputs(cardano.NewTxOutput(rAddr, cardano.NewValue(17811223)))
    txBuilder.SetFee(188777) //
    txBuilder.AddCertificate(stakeDelCert)
    txBuilder.AddNativeScript(n)
    txBuilder.AddNativeScript(s)
    txBuilder.Sign(
       crypto.PrvKey(p1),
       crypto.PrvKey(p2),
       crypto.PrvKey(s1),
       crypto.PrvKey(s2),
    )
    fmt.Println(hex.EncodeToString(p1.Public().PublicKey()), hex.EncodeToString(p2.Public().PublicKey()))
    tx, err := txBuilder.Build()
    if err != nil {
       panic(err)
    }
    fmt.Println(fmt.Sprintf("%+v", tx))
    txHash, err := SubmitTx(tx)
    if err != nil {
       panic(err)
    }
    fmt.Println(txHash)
}

func getRootKey(mnemonic string) bip32.XPrv {
    entropy, err := bip39.EntropyFromMnemonic(mnemonic)
    if err != nil {
       panic(err)
    }
    rootKey := bip32.FromBip39Entropy(
       entropy,
       []byte{},
    )

    return rootKey
}

func getPaymentPrvKey(rootKey bip32.XPrv) bip32.XPrv {
    accountKey := rootKey.Derive(harden(1852)).Derive(harden(1815)).Derive(harden(0))
    paymentKey := accountKey.Derive(0).Derive(0)
    return paymentKey
}

func getStakePrvKey(rootKey bip32.XPrv) bip32.XPrv {
    accountKey := rootKey.Derive(harden(1852)).Derive(harden(1815)).Derive(harden(0))
    stakeKey := accountKey.Derive(2).Derive(0)
    return stakeKey
}

errorMsg :

map[error:Bad Request message:{"contents":{"contents":{"contents":{"era":"ShelleyBasedEraBabbage","error":[{"contents":{"contents":{"contents":{"contents":["74725e00e926bd68e1e787e5f1f57b35c059d95002f8ad33d420050e"],"tag":"ExtraneousScriptWitnessesUTXOW"},"tag":"ShelleyInAlonzoUtxowPredFailure"},"tag":"AlonzoInBabbageUtxowPredFailure"},"tag":"UtxowFailure"}],"kind":"ShelleyTxValidationError"},"tag":"TxValidationErrorInCardanoMode"},"tag":"TxCmdTxSubmitValidationError"},"tag":"TxSubmitFail"} status_code:400]